From 83065c529895eedd7491662908eb27e7e4beb1ae Mon Sep 17 00:00:00 2001 From: Leonardo Fagundes Luz Serrano Date: Mon, 17 Jan 2022 18:30:04 +0000 Subject: [PATCH] Add debian package for Ceph Add debian packaging infrastructure for integ/ceph to build a debian package. Test Plan: build-pkg; build-image; same contents as RPM PASS build-pkg PASS build-image PASS same contents and permissions as RPM Attention: In order to avoid memory issues during the build, please do one of the following: - Developers with only 32G RAM will need to temporarily unmount /var/lib/sbuild/build so that the build system uses the disk instead of tmpfs OR - update /etc/fstab to set the size for the sbuild tmpfs filesystem in the pkgbuilder container: tmpfs /var/lib/sbuild/build tmpfs uid=sbuild,gid=sbuild,mode=2770,size=40G 0 0 Note: Build times can be long. In order to accelerate it, adjust the values of MINIKUBECPUS/MINIKUBEMEMORY in import-stx file (tools repo) before building the containers with stx-init-env. Depends-On: https://review.opendev.org/c/starlingx/tools/+/827884 Story: 2009101 Task: 44304 Signed-off-by: Leonardo Fagundes Luz Serrano Change-Id: Idc8ee1ebac5c973622c1c599f4a04c001bfa89a6 --- ceph/ceph/debian/deb_folder/README.Debian | 120 + .../debian/deb_folder/calc-max-parallel.sh | 32 + .../debian/deb_folder/ceph-base.ceph.init | 1154 ++ ceph/ceph/debian/deb_folder/ceph-base.dirs | 9 + ceph/ceph/debian/deb_folder/ceph-base.docs | 1 + ceph/ceph/debian/deb_folder/ceph-base.install | 42 + .../deb_folder/ceph-base.lintian-overrides | 1 + .../ceph/debian/deb_folder/ceph-base.postinst | 61 + ceph/ceph/debian/deb_folder/ceph-base.postrm | 13 + ceph/ceph/debian/deb_folder/ceph-common.dirs | 3 + .../debian/deb_folder/ceph-common.install | 52 + .../deb_folder/ceph-common.lintian-overrides | 1 + .../debian/deb_folder/ceph-common.manpages | 1 + .../debian/deb_folder/ceph-common.postinst | 140 + .../ceph/debian/deb_folder/ceph-common.postrm | 74 + .../debian/deb_folder/ceph-common.preinst | 29 + .../debian/deb_folder/ceph-common.rbdmap.init | 56 + .../debian/deb_folder/ceph-fs-common.install | 4 + ceph/ceph/debian/deb_folder/ceph-fuse.install | 4 + .../ceph/debian/deb_folder/ceph-fuse.manpages | 1 + .../deb_folder/ceph-grafana-dashboards.dirs | 3 + .../ceph-grafana-dashboards.install | 3 + ceph/ceph/debian/deb_folder/ceph-mds.dirs | 1 + ceph/ceph/debian/deb_folder/ceph-mds.install | 3 + ceph/ceph/debian/deb_folder/ceph-mds.postinst | 47 + .../deb_folder/ceph-mgr-dashboard.install | 1 + .../deb_folder/ceph-mgr-dashboard.postinst | 43 + .../ceph-mgr-diskprediction-cloud.install | 1 + .../ceph-mgr-diskprediction-cloud.postinst | 43 + .../ceph-mgr-diskprediction-local.install | 1 + .../ceph-mgr-diskprediction-local.postinst | 43 + .../deb_folder/ceph-mgr-k8sevents.install | 1 + .../deb_folder/ceph-mgr-k8sevents.postinst | 43 + .../debian/deb_folder/ceph-mgr-rook.install | 1 + .../debian/deb_folder/ceph-mgr-rook.postinst | 43 + .../debian/deb_folder/ceph-mgr-ssh.install | 1 + .../debian/deb_folder/ceph-mgr-ssh.postinst | 43 + ceph/ceph/debian/deb_folder/ceph-mgr.dirs | 1 + ceph/ceph/debian/deb_folder/ceph-mgr.install | 32 + ceph/ceph/debian/deb_folder/ceph-mgr.postinst | 51 + ceph/ceph/debian/deb_folder/ceph-mon.dirs | 1 + ceph/ceph/debian/deb_folder/ceph-mon.install | 7 + ceph/ceph/debian/deb_folder/ceph-mon.postinst | 46 + ceph/ceph/debian/deb_folder/ceph-osd.dirs | 2 + ceph/ceph/debian/deb_folder/ceph-osd.install | 31 + .../deb_folder/ceph-resource-agents.install | 1 + ceph/ceph/debian/deb_folder/ceph.NEWS | 180 + .../debian/deb_folder/ceph.lintian-overrides | 1 + .../debian/deb_folder/cephfs-shell.install | 2 + ceph/ceph/debian/deb_folder/changelog | 883 ++ ceph/ceph/debian/deb_folder/clean | 32 + ceph/ceph/debian/deb_folder/compat | 1 + ceph/ceph/debian/deb_folder/control | 849 ++ ceph/ceph/debian/deb_folder/copyright | 977 ++ .../deb_folder/lib-systemd/system-sleep/ceph | 12 + .../system/ceph-create-keys.service | 9 + .../lib-systemd/system/ceph-mds.service | 16 + .../lib-systemd/system/ceph-mon.service | 22 + .../lib-systemd/system/ceph-osd@.service | 22 + .../debian/deb_folder/libcephfs-dev.install | 2 + .../debian/deb_folder/libcephfs-java.jlibs | 1 + .../debian/deb_folder/libcephfs-jni.install | 1 + .../libcephfs-jni.lintian-overrides | 2 + .../ceph/debian/deb_folder/libcephfs2.install | 1 + .../debian/deb_folder/librados-dev.install | 5 + ceph/ceph/debian/deb_folder/librados2.install | 2 + .../debian/deb_folder/libradospp-dev.install | 8 + .../deb_folder/libradosstriper-dev.install | 3 + .../deb_folder/libradosstriper1.install | 1 + .../ceph/debian/deb_folder/librbd-dev.install | 4 + ceph/ceph/debian/deb_folder/librbd1.install | 1 + .../ceph/debian/deb_folder/librgw-dev.install | 5 + ceph/ceph/debian/deb_folder/librgw2.install | 2 + .../deb_folder/man/ceph-crush-location.1 | 24 + .../debian/deb_folder/man/mount.fuse.ceph.8 | 30 + .../deb_folder/missing-sources/bootstrap.js | 2377 ++++ .../debian/deb_folder/missing-sources/two.js | 10244 ++++++++++++++++ .../deb_folder/python3-ceph-argparse.install | 2 + .../debian/deb_folder/python3-cephfs.install | 3 + .../debian/deb_folder/python3-rados.install | 2 + .../debian/deb_folder/python3-rbd.install | 2 + .../debian/deb_folder/python3-rgw.install | 2 + .../deb_folder/rados-objclass-dev.install | 1 + ceph/ceph/debian/deb_folder/radosgw.dirs | 4 + ceph/ceph/debian/deb_folder/radosgw.install | 17 + ceph/ceph/debian/deb_folder/radosgw.postinst | 17 + ceph/ceph/debian/deb_folder/radosgw.prerm | 22 + ceph/ceph/debian/deb_folder/rbd-fuse.install | 2 + .../ceph/debian/deb_folder/rbd-mirror.install | 5 + ceph/ceph/debian/deb_folder/rbd-nbd.install | 2 + .../ceph/debian/deb_folder/rest-bench.install | 1 + ceph/ceph/debian/deb_folder/rules | 289 + .../deb_folder/source.lintian-overrides | 16 + ceph/ceph/debian/deb_folder/source/format | 1 + ceph/ceph/debian/deb_folder/source/options | 11 + ceph/ceph/debian/deb_folder/tests/build-rados | 31 + ceph/ceph/debian/deb_folder/tests/build-rbd | 24 + ceph/ceph/debian/deb_folder/tests/ceph-client | 11 + ceph/ceph/debian/deb_folder/tests/control | 9 + ceph/ceph/debian/deb_folder/tests/python-ceph | 7 + .../deb_folder/udev/95-ceph-osd-lvm.rules | 13 + .../deb_folder/workarounds/ceph-dencoder-oom | 7 + ceph/ceph/debian/dl_hook | 101 + ceph/ceph/debian/meta_data.yaml | 108 + ...ix-error-related-to-src-.git_version.patch | 34 + ...rc-CMakeLists.txts-DESTINATION-error.patch | 166 + .../patches/32bit-avoid-overloading.patch | 23 + .../debian/patches/32bit-avoid-size_t.patch | 112 + .../add-option-to-disable-ceph-dencoder.patch | 11 + .../debian/patches/allow-bgp-to-host.patch | 18 + .../patches/another-cmakelists-fix.patch | 35 + .../patches/bluefs-use-uint64_t-for-len.patch | 58 + ...etweb-755-1.8-somaxconn-configurable.patch | 53 + ...-755-1.8-somaxconn-configurable_conf.patch | 18 + ...-755-1.8-somaxconn-configurable_test.patch | 17 + .../cmake_add_1.74_to_known_versions.patch | 52 + ...UTOR_AS_DEFAULT_for_Boost.Asio_users.patch | 30 + .../debian-armel-armhf-buildflags.patch | 45 + ceph/ceph/debian/patches/disable-crypto.patch | 16 + .../patches/fix-bash-completion-location | 9 + .../patches/fix-ceph-osd-systemd-target.patch | 17 + .../patches/make-ceph-python-3.9-aware.patch | 28 + .../patches/mds-purgequeue-use_uint64_t.patch | 31 + .../debian/patches/riscv64-link-pthread.patch | 16 + ceph/ceph/debian/patches/series | 24 + .../update-java-source-target-flags.patch | 23 + 126 files changed, 19556 insertions(+) create mode 100644 ceph/ceph/debian/deb_folder/README.Debian create mode 100755 ceph/ceph/debian/deb_folder/calc-max-parallel.sh create mode 100755 ceph/ceph/debian/deb_folder/ceph-base.ceph.init create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.docs create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.lintian-overrides create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-base.postrm create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.dirs create mode 100755 ceph/ceph/debian/deb_folder/ceph-common.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.lintian-overrides create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.manpages create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.postrm create mode 100644 ceph/ceph/debian/deb_folder/ceph-common.preinst create mode 100755 ceph/ceph/debian/deb_folder/ceph-common.rbdmap.init create mode 100644 ceph/ceph/debian/deb_folder/ceph-fs-common.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-fuse.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-fuse.manpages create mode 100644 ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mds.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-mds.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mds.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-rook.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-rook.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-ssh.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr-ssh.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mgr.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-mon.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-mon.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-mon.postinst create mode 100644 ceph/ceph/debian/deb_folder/ceph-osd.dirs create mode 100644 ceph/ceph/debian/deb_folder/ceph-osd.install create mode 100644 ceph/ceph/debian/deb_folder/ceph-resource-agents.install create mode 100644 ceph/ceph/debian/deb_folder/ceph.NEWS create mode 100644 ceph/ceph/debian/deb_folder/ceph.lintian-overrides create mode 100644 ceph/ceph/debian/deb_folder/cephfs-shell.install create mode 100644 ceph/ceph/debian/deb_folder/changelog create mode 100644 ceph/ceph/debian/deb_folder/clean create mode 100644 ceph/ceph/debian/deb_folder/compat create mode 100644 ceph/ceph/debian/deb_folder/control create mode 100644 ceph/ceph/debian/deb_folder/copyright create mode 100755 ceph/ceph/debian/deb_folder/lib-systemd/system-sleep/ceph create mode 100644 ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-create-keys.service create mode 100644 ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mds.service create mode 100644 ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mon.service create mode 100644 ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-osd@.service create mode 100644 ceph/ceph/debian/deb_folder/libcephfs-dev.install create mode 100644 ceph/ceph/debian/deb_folder/libcephfs-java.jlibs create mode 100644 ceph/ceph/debian/deb_folder/libcephfs-jni.install create mode 100644 ceph/ceph/debian/deb_folder/libcephfs-jni.lintian-overrides create mode 100644 ceph/ceph/debian/deb_folder/libcephfs2.install create mode 100644 ceph/ceph/debian/deb_folder/librados-dev.install create mode 100644 ceph/ceph/debian/deb_folder/librados2.install create mode 100644 ceph/ceph/debian/deb_folder/libradospp-dev.install create mode 100644 ceph/ceph/debian/deb_folder/libradosstriper-dev.install create mode 100644 ceph/ceph/debian/deb_folder/libradosstriper1.install create mode 100644 ceph/ceph/debian/deb_folder/librbd-dev.install create mode 100644 ceph/ceph/debian/deb_folder/librbd1.install create mode 100644 ceph/ceph/debian/deb_folder/librgw-dev.install create mode 100644 ceph/ceph/debian/deb_folder/librgw2.install create mode 100644 ceph/ceph/debian/deb_folder/man/ceph-crush-location.1 create mode 100644 ceph/ceph/debian/deb_folder/man/mount.fuse.ceph.8 create mode 100644 ceph/ceph/debian/deb_folder/missing-sources/bootstrap.js create mode 100644 ceph/ceph/debian/deb_folder/missing-sources/two.js create mode 100644 ceph/ceph/debian/deb_folder/python3-ceph-argparse.install create mode 100644 ceph/ceph/debian/deb_folder/python3-cephfs.install create mode 100644 ceph/ceph/debian/deb_folder/python3-rados.install create mode 100644 ceph/ceph/debian/deb_folder/python3-rbd.install create mode 100644 ceph/ceph/debian/deb_folder/python3-rgw.install create mode 100644 ceph/ceph/debian/deb_folder/rados-objclass-dev.install create mode 100644 ceph/ceph/debian/deb_folder/radosgw.dirs create mode 100644 ceph/ceph/debian/deb_folder/radosgw.install create mode 100644 ceph/ceph/debian/deb_folder/radosgw.postinst create mode 100644 ceph/ceph/debian/deb_folder/radosgw.prerm create mode 100644 ceph/ceph/debian/deb_folder/rbd-fuse.install create mode 100644 ceph/ceph/debian/deb_folder/rbd-mirror.install create mode 100644 ceph/ceph/debian/deb_folder/rbd-nbd.install create mode 100644 ceph/ceph/debian/deb_folder/rest-bench.install create mode 100755 ceph/ceph/debian/deb_folder/rules create mode 100644 ceph/ceph/debian/deb_folder/source.lintian-overrides create mode 100644 ceph/ceph/debian/deb_folder/source/format create mode 100644 ceph/ceph/debian/deb_folder/source/options create mode 100755 ceph/ceph/debian/deb_folder/tests/build-rados create mode 100755 ceph/ceph/debian/deb_folder/tests/build-rbd create mode 100755 ceph/ceph/debian/deb_folder/tests/ceph-client create mode 100644 ceph/ceph/debian/deb_folder/tests/control create mode 100755 ceph/ceph/debian/deb_folder/tests/python-ceph create mode 100644 ceph/ceph/debian/deb_folder/udev/95-ceph-osd-lvm.rules create mode 100644 ceph/ceph/debian/deb_folder/workarounds/ceph-dencoder-oom create mode 100755 ceph/ceph/debian/dl_hook create mode 100644 ceph/ceph/debian/meta_data.yaml create mode 100644 ceph/ceph/debian/patches/0001-Fix-error-related-to-src-.git_version.patch create mode 100644 ceph/ceph/debian/patches/0002-Fix-src-CMakeLists.txts-DESTINATION-error.patch create mode 100644 ceph/ceph/debian/patches/32bit-avoid-overloading.patch create mode 100644 ceph/ceph/debian/patches/32bit-avoid-size_t.patch create mode 100644 ceph/ceph/debian/patches/add-option-to-disable-ceph-dencoder.patch create mode 100644 ceph/ceph/debian/patches/allow-bgp-to-host.patch create mode 100644 ceph/ceph/debian/patches/another-cmakelists-fix.patch create mode 100644 ceph/ceph/debian/patches/bluefs-use-uint64_t-for-len.patch create mode 100644 ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable.patch create mode 100644 ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch create mode 100644 ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch create mode 100644 ceph/ceph/debian/patches/cmake_add_1.74_to_known_versions.patch create mode 100644 ceph/ceph/debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch create mode 100644 ceph/ceph/debian/patches/debian-armel-armhf-buildflags.patch create mode 100644 ceph/ceph/debian/patches/disable-crypto.patch create mode 100644 ceph/ceph/debian/patches/fix-bash-completion-location create mode 100644 ceph/ceph/debian/patches/fix-ceph-osd-systemd-target.patch create mode 100644 ceph/ceph/debian/patches/make-ceph-python-3.9-aware.patch create mode 100644 ceph/ceph/debian/patches/mds-purgequeue-use_uint64_t.patch create mode 100644 ceph/ceph/debian/patches/riscv64-link-pthread.patch create mode 100644 ceph/ceph/debian/patches/series create mode 100644 ceph/ceph/debian/patches/update-java-source-target-flags.patch diff --git a/ceph/ceph/debian/deb_folder/README.Debian b/ceph/ceph/debian/deb_folder/README.Debian new file mode 100644 index 000000000..be21ad791 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/README.Debian @@ -0,0 +1,120 @@ +## See online installation and setup documentation at + + http://ceph.com/docs/master/install/manual-deployment/ + +-------- -------- -------- + +## "systemd" requires manual activation of services: + + ## MON + # systemctl start ceph-mon + # systemctl enable ceph-mon + + ## OSD.0 (set other OSDs like this) + # systemctl start ceph-osd@0 + # systemctl enable ceph-osd@0 + + ## MDS + # systemctl start ceph-mds + # systemctl enable ceph-mds + + ## "ceph" meta-service (starts/stops all the above like old init script) + # systemctl start ceph + # systemctl enable ceph + + The ceph cluster can be set in the "/etc/default/ceph" file + by setting the CLUSTER environment variable. + +-------- -------- -------- + +## Upgrade procedure (0.72.2 to 0.80): + + * Read "Upgrade Sequencing" in release notes: + + http://ceph.com/docs/firefly/release-notes/ + + * Upgrade packages. + + * Restart MONs. + + * Restart all OSDs. + + * Run `ceph osd crush tunables default`. + + * (Restart MDSes). + + * Consider setting the 'hashpspool' flag on your pools (new default): + + ceph osd pool set {pool} hashpspool true + + This changes the pool to use a new hashing algorithm for the distribution of + Placement Groups (PGs) to OSDs. This new algorithm ensures a better distribution + to all OSDs. Be aware that this change will temporarly put some of your PGs into + "misplaced" state and cause additional I/O until all PGs are moved to their new + location. See http://tracker.ceph.com/issues/4128 for the details about the new + algorithm. + + Read more about tunables in + + http://ceph.com/docs/master/rados/operations/crush-map/#tunables + + Upgrading all OSDs and setting correct tunables is necessary to avoid the errors like: + + ## rbdmap errors: + libceph: mon2 192.168.0.222:6789 socket error on read + + Wrong tunables may produce the following error: + + libceph: mon0 192.168.0.222:6789 socket error on read + libceph: mon2 192.168.0.250:6789 feature set mismatch, my 4a042a42 < server's 2004a042a42, missing 20000000000 + + ## MDS errors: + one or more OSDs do not support TMAP2OMAP; upgrade OSDs before starting MDS (or downgrade MDS) + + See also: + + http://ceph.com/docs/firefly/install/upgrading-ceph/ + +-------- -------- -------- + + Jerasure pool(s) will bump requirements to Linux_3.15 (not yet released) for + kernel CephFS and RBD clients. + +-------- -------- -------- + + RBD kernel driver do not support authentication so the following setting + in "/etc/ceph/ceph.conf" may be used to relax client auth. requirements: + + cephx cluster require signatures = true + cephx service require signatures = false + +-------- -------- -------- + +> How to mount CephFS using fuse client from "/etc/fstab"? + + Add (and modify) the following sample to "/etc/fstab": + + mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0 + + This is equivalent of running + + ceph-fuse /mnt/ceph --id=admin -o noatime,allow_other + + as root. + +-------- -------- -------- + + To avoid known issue with kernel FS client it is recommended to use + 'readdir_max_entries' mount option, for example: + + mount -t ceph 1.2.3.4:/ /mnt/ceph -o readdir_max_entries=64 + +-------- -------- -------- + + Beware of "mlocate" scanning of OSD file systems. To avoid problems add + "/var/lib/ceph" to PRUNEPATHS in the "/etc/updatedb.conf" like in the + following example: + + PRUNEPATHS="/tmp /var/spool /media /mnt /var/lib/ceph" + +-------- -------- -------- diff --git a/ceph/ceph/debian/deb_folder/calc-max-parallel.sh b/ceph/ceph/debian/deb_folder/calc-max-parallel.sh new file mode 100755 index 000000000..3593955ca --- /dev/null +++ b/ceph/ceph/debian/deb_folder/calc-max-parallel.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Simple tool to calculate max parallel jobs based on +# memory of builder. +# +# MDCache.cc generally runs out of RAM in 4G of memory +# with parallel=4 + +total_ram=$(grep MemTotal /proc/meminfo | awk '{ print $2 }') + +sixtyfour_g=$((64*1024*1024)) +fourtyheight_g=$((48*1024*1024)) +thirtytwo_g=$((32*1024*1024)) +sixteen_g=$((16*1024*1024)) +eight_g=$((8*1024*1024)) +four_g=$((4*1024*1024)) + +if [ ${total_ram} -le ${four_g} ]; then + echo "--max-parallel=1" +elif [ ${total_ram} -le ${eight_g} ]; then + echo "--max-parallel=2" +elif [ ${total_ram} -le ${sixteen_g} ]; then + echo "--max-parallel=3" +elif [ ${total_ram} -le ${thirtytwo_g} ]; then + echo "--max-parallel=6" +elif [ ${total_ram} -le ${fourtyheight_g} ]; then + echo "--max-parallel=8" +elif [ ${total_ram} -le ${sixtyfour_g} ]; then + echo "--max-parallel=12" +else + echo "--max-parallel=16" +fi diff --git a/ceph/ceph/debian/deb_folder/ceph-base.ceph.init b/ceph/ceph/debian/deb_folder/ceph-base.ceph.init new file mode 100755 index 000000000..b15301b5f --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.ceph.init @@ -0,0 +1,1154 @@ +#!/bin/sh +# Start/stop ceph daemons +# chkconfig: 2345 60 80 + +### BEGIN INIT INFO +# Provides: ceph +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Required-Start: $remote_fs $named $network $time +# Required-Stop: $remote_fs $named $network $time +# Short-Description: Start Ceph distributed file system daemons at boot time +# Description: Enable Ceph distributed file system services. +### END INIT INFO + +# TODO: on FreeBSD/OSX, use equivalent script file +if [ -e /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions +fi + +if [ `dirname $0` = "." ] && [ $PWD != "/etc/init.d" ]; then + # looks like an autotools src dir build + BINDIR=. + SBINDIR=. + LIBDIR=. + LIBEXECDIR=. + ETCDIR=. + ASSUME_DEV=1 +else + if [ -e CMakeCache.txt ] && [ -e bin/init-ceph ]; then + # looks like a cmake build directory + CEPH_ROOT=`grep ceph_SOURCE_DIR CMakeCache.txt | cut -d "=" -f 2` + BINDIR=bin + SBINDIR=bin + LIBEXECDIR=$CEPH_ROOT/src + ETCDIR=. + ASSUME_DEV=1 + CEPH_LIB=$CEPH_ROOT/build/lib + echo "$PYTHONPATH" | grep -q $CEPH_LIB || export PYTHONPATH=$CEPH_LIB/cython_modules/lib.@MGR_PYTHON_VERSION_MAJOR@:$PYTHONPATH + echo "$LD_LIBRARY_PATH" | grep -q $CEPH_LIB || export LD_LIBRARY_PATH=$CEPH_LIB:$LD_LIBRARY_PATH + echo "$DYLD_LIBRARY_PATH" | grep -q $CEPH_LIB || export DYLD_LIBRARY_PATH=$CEPH_LIB:$DYLD_LIBRARY_PATH + else + BINDIR=/usr/bin + SBINDIR=/usr/sbin + LIBDIR=/usr/lib64/ceph + LIBEXECDIR=$LIBDIR + ETCDIR=/etc/ceph + ASSUME_DEV=0 + fi +fi + +if [ -n "$CEPH_BIN" ] && [ -n "$CEPH_ROOT" ] && [ -n "$CEPH_BUILD_DIR" ]; then + BINDIR=$CEPH_BIN + SBINDIR=$CEPH_ROOT/src + ETCDIR=$CEPH_BIN + LIBEXECDIR=$CEPH_ROOT/src + ASSUME_DEV=1 +fi + +#################################################### +#### StarlingX: Check if a ceph process is hung #### +#################################################### + +# Each Ceph process has a state and a status. +# - States are used by the this script for decisions +# - Statuses are $UP or $DOWN and represent a process working state as reported by Ceph +# OSD processes can have a status of "up" or "down" based on what Ceph is +# reporting. We ignore the "in" status (we consider it as "Down") +# Ceph processes states: +# 1. STARTUP - a process is started but has not yet joined the cluster +# 2. OPERATIONAL - a process joined the cluster +# 3. HANGED - a process is hung +# 4. STOPPED - a process is offline (/etc/init.d start was not executed) +# Monitoring is done differently based on state +# All timers are in seconds + +# FSM states +ST_STARTUP="STARTED" +ST_OPER="OPERATIONAL" +ST_HANGED="HANGED" +ST_STOPPED="STOPPED" + +# Ceph OSD status +UP="up" +DOWN="down" + +#paths +source /usr/bin/tsconfig +DATA_PATH=$VOLATILE_PATH/ceph_hang # folder where we keep sate information +LOG_PATH=/var/log/ceph +LOG_FILE=$LOG_PATH/ceph-process-states.log +mkdir -p $DATA_PATH # make sure folder exists + +#timeouts +WAIT_FOR_CMD=10 # max wait for a response from Ceph +WAIT_FOR_OSD_OPERATIONAL=300 # max wait for OSD to go 'up' when in startup state +WAIT_FOR_OSD_DOWN_CONFIRM=300 # even if OSD is down it may be flapping, wait for a while +WAIT_FOR_MON_OPERATIONAL=60 # max wait for MON to go 'up' when in startup state +WAIT_FOR_MON_DOWN_CONFIRM=300 # even if MON is down it may be flapping, wait for a while + +# Blocked Ops Detection (values in seconds) +BLOCKED_OPS_DETECTION_ENABLED="true" # Enable/Disable detection +BLOCKED_OPS_START_DETECTION=300 # Wait on OSD startup before handling blocked ops +BLOCKED_OPS_RESTART_THRESH=480 # Restart OSD if blocked reqs are longer that this + +# Stuck Peering Detection (values in seconds) +STUCK_PEERING_DETECTION_ENABLED="true" # Enable/Disable detection +STUCK_PEERING_START_DETECTION=300 # Wait on OSD startup before handling stuck peering +STUCK_PEERING_RESTART_THRESH=180 # Restart OSD if stuck peering is longer that this + +LOG_LEVEL=NORMAL # DEBUG + +save_proc_state() { + # Set the state of a process in the state machine and store it in a file + local name=$1 + local state=$2 + if [ "$state" != "$ST_STARTUP" ] && [ "$state" != "$ST_OPER" ] && \ + [ "$state" != "$ST_HANGED" ] && [ "$state" != "$ST_STOPPED" ]; then + wlog $name "ERROR" "State $state is invalid, resetting to $ST_STARTUP" print_trace + state=$ST_STARTUP + fi + echo "$state" > ${DATA_PATH}/.${name}_state +} + +load_proc_state() { + # Get the state of the state machine from file and validate content + local name=$1 + local state=$ST_STARTUP + if [ -f ${DATA_PATH}/.${name}_state ]; then + state=$(cat ${DATA_PATH}/.${name}_state) + fi + if [ "$state" != "$ST_STARTUP" ] && [ "$state" != "$ST_OPER" ] && \ + [ "$state" != "$ST_HANGED" ] && [ "$state" != "$ST_STOPPED" ]; then + wlog $name "ERROR" "State $state is invalid, resetting to $ST_STARTUP" print_trace + state=$ST_STARTUP + save_proc_state $name $state + fi + echo "$state"; return +} + +save_proc_startup_ok() { + # Reset to initial state after a process started successfully (i.e. it has a valid pid) + local name=$1 + + # Process just started, clear all records, reset state + rm -f ${DATA_PATH}/.${name}_start_time + rm -f ${DATA_PATH}/.${name}_down_time + save_proc_state $name $ST_STARTUP + + # Save the time when a process was started + wlog $name INFO "Process $ST_STARTUP successfully, waiting for it to become $ST_OPER" + echo $(date +%s) > ${DATA_PATH}/.${name}_start_time +} + +save_proc_status() { + # Store the status of a process ($UP or $DOWN) and its start time + # Note that a process status is different from its states + local name=$1 + local status=$2 + if [ "$status" == $UP ]; then + rm -f ${DATA_PATH}/.${name}_down_time + else + if [ ! -f ${DATA_PATH}/.${name}_down_time ]; then + echo $(date +%s) > ${DATA_PATH}/.${name}_down_time + fi + fi +} + +load_proc_status() { + # Load a process status ($UP or $DOWN) + local name=$1 + if [ -f ${DATA_PATH}/.${name}_down_time ]; then + echo $DOWN + else + echo $UP + fi +} + +get_duration(){ + # Get duration based on file record and current time + local name=$1 + local record=$2 # filename with prev time + + # Check that we have a filename + if [ ! -f $record ]; then + wlog $name ERROR "Failed to compute duration, time was never stored in $record!" print_trace + echo "-1"; return + fi + + # Get and validate time previously saved in file + local start_time=$(cat $record) + re="^[0-9]+$" + if ! [[ "$start_time" =~ $re ]] ; then + wlog $name ERROR "Recorded time '$start_time' is not a number!" print_trace + echo "-1"; return + fi + + # Compute duration + local now=$(date +%s) + local duration=$((now-start_time)) + if [ "$duration" -lt 0 ]; then + wlog $name ERROR "Duration less than 0!" print_trace + echo "-1"; return + fi + echo $duration +} + +get_proc_run_time() { + local name=$1 + local time=$(get_duration $name ${DATA_PATH}/.${name}_start_time) + wlog $name DEBUG ">>> process running for: ${time}s" + echo $time +} + +get_proc_down_time() { + local name=$1 + local time=$(get_duration $name ${DATA_PATH}/.${name}_down_time) + wlog $name DEBUG ">>> process down for: ${time}s" + echo $time +} + +run_state_machine() { + # Small state machine, returns process state as defined in ST_* constants + # Same logic apply to both ceph-osd and ceph-mon, only timeouts are different + local name=$1 # 'osd.' or 'mon.' + local type=$2 # 'osd' or 'mon' + local status=$3 # daemon current status ($UP or $DOWN) + local wait_for_operational=$4 # how much time to wait for a process to go up + local wait_for_down_confirm=$5 # how much time to wait before reporting a process as down + + local state=$(load_proc_state $name) + + wlog $name "DEBUG" ">>> state: $state" + # state machine + if [ "$state" = "$ST_STARTUP" ]; then + wlog $name "DEBUG" ">>> status: $status" + if [ "$status" = $UP ]; then + save_proc_state $name $ST_OPER + wlog $name "INFO" "Process is OPERATIONAL" + echo $ST_OPER; return + else + # the process should be 'up' in $WAIT_FOR_OSD_OPERATIONAL seconds! + if [ $(get_proc_run_time $name) -gt $wait_for_operational ]; then + # process hung! + wlog $name "ERROR" "Process failed to go up in ${wait_for_operational}s after start, reporting it as $ST_HANGED!" + save_proc_state $name $ST_HANGED + echo $ST_HANGED; return + fi + fi + elif [ "$state" = "$ST_OPER" ]; then + if [ "$status" = "$DOWN" ]; then + if [ $(load_proc_status $name) = $UP ];then + wlog $name "WARN" "Process went down!" + save_proc_status $name $DOWN + fi + # if a process is down we don't report it as hung for a while + # this should avoid status flapping + if [ $(get_proc_down_time $name) -gt $wait_for_down_confirm ]; then + # the process is down for a long time, report it as hung! + wlog $name "ERROR" "Process went down for more than ${wait_for_down_confirm}s, reporting it as $ST_HANGED" + save_proc_state $name $ST_HANGED + echo $ST_HANGED; return + fi + elif [ "$status" = "$UP" ]; then + if [ $(load_proc_status $name) = $DOWN ]; then + wlog $name "WARN" "Process went up, flapping status or busy process?" + save_proc_status $name $UP + return + fi + fi + elif [ "$state" = "$ST_HANGED" ] || [ "$state" = "$ST_STOPPED" ]; then + # nothing to do, resetting from these states is done externally in /etc/ceph/ceph_pmon_wrapper.sh + echo $state; return + fi +} + +CEPH_FAILURE="" +execute_ceph_cmd() { + # execute a comand and in case it timeouts mark ceph as failed + local name=$1 + local cmd=$2 + local cmd="timeout $WAIT_FOR_CMD $cmd" + set -o pipefail + eval "$cmd >$DATA_PATH/.ceph_cmd_out" + errcode=$? + set +o pipefail + if [ -z "$output" ] && [ $errcode -eq 124 ]; then # 'timeout' returns 124 when timing out + wlog $name "WARN" "Ceph cluster failed to respond in ${WAIT_FOR_CMD}s when running: $cmd" + CEPH_FAILURE="true" + echo ""; return 1 + fi + output=$(cat $DATA_PATH/.ceph_cmd_out) + if [ -z "$output" ] || [ $errcode -ne 0 ]; then + wlog $name "WARN" "Error executing: $cmd errorcode: $errcode output: $output" + echo ""; return 1 + fi + echo "$output"; return $errcode +} + +CEPH_OSD_TREE="" +CEPH_HEALTH_DETAIL="" +is_process_hung() { + local name=$1 + local type=$2 # 'osd' or 'mon' + + # Abort if we had previous errors with Ceph + if [ "$CEPH_FAILURE" = "true" ]; then + wlog $name "WARN" "Ceph cluster is marked as failed, aborting hang check" + echo "false"; return + fi + + # Cache Ceph Health for later use as calling Ceph takes time + if [ -z "$CEPH_HEALTH_DETAIL" ]; then + execute_ceph_cmd CEPH_HEALTH_DETAIL $name "ceph health detail" + if [ $? -ne 0 ]; then + wlog $name "WARN" "Aborting hang check" + echo "false"; return + fi + fi + + # Check if an OSD is hung + if [ "$type" = "osd" ]; then + # Ignore health check if OSDs are administratively down + # Note this can be done with: 'ceph osd set noup; ceph osd down ' + $(echo "$CEPH_HEALTH_DETAIL" | grep -q "noup.*set") + if [ $? -eq 0 ]; then + wlog $name "WARN" "Ceph 'noup' flag is set, aborting hang check" + echo "false"; return + fi + + # Multiple OSD processes may be running, so we only run + # 'ceph osd tree' once as it takes some time to execute + if [ -z "$CEPH_OSD_TREE" ]; then + execute_ceph_cmd CEPH_OSD_TREE $name "ceph osd tree" + if [ $? -ne 0 ]; then + wlog $name "WARN" "Ceph cmd exec failed, aborting hang check" + echo "false"; return + fi + fi + + # Get osd status as 'up' or, for any other output, as 'down' + echo "$CEPH_OSD_TREE" | grep $name | grep -q "up" + if [ "$?" -eq 0 ]; then + osd_status=$UP + else + osd_status=$DOWN + fi + + local state=$(run_state_machine $name $type $osd_status \ + $WAIT_FOR_OSD_OPERATIONAL $WAIT_FOR_OSD_DOWN_CONFIRM) + if [ "$state" = "$ST_HANGED" ]; then + echo "true"; return + else + echo "false"; return + fi + + + # Check if a Monitor is hung + elif [ "$type" = "mon" ]; then + # Get monitor status info + local mon_status=$UP + echo "$CEPH_HEALTH_DETAIL" | grep -q -e "^[[:space:]]*$name.*down" + if [ $? -eq 0 ]; then + mon_status=$DOWN + fi + + local state=$(run_state_machine $name $type $mon_status \ + $WAIT_FOR_MON_OPERATIONAL $WAIT_FOR_MON_DOWN_CONFIRM) + if [ "$state" = "$ST_HANGED" ]; then + echo "true"; return + else + echo "false"; return + fi + + else + wlog $name "WARN" "Unknown process type: $type" + fi + echo "false" +} + +osd_has_blocked_ops() { + local name=$1 + + # Abort if we had previous errors with Ceph + if [ "$CEPH_FAILURE" = "true" ]; then + wlog $name "WARN" "Ceph cluster is marked as failed, aborting blocked ops check" + echo "false"; return + fi + + # Cache Ceph Health for later use as calling Ceph takes time This is + # initially cached from the hang check but check and call again here if + # needed + if [ -z "$CEPH_HEALTH_DETAIL" ]; then + execute_ceph_cmd CEPH_HEALTH_DETAIL $name "ceph health detail" + if [ $? -ne 0 ]; then + wlog $name "WARN" "Aborting blocked ops check" + echo "false"; return + fi + fi + + # Ignore health check if OSDs are administratively down + # Note this can be done with: 'ceph osd set noup; ceph osd down ' + $(echo "$CEPH_HEALTH_DETAIL" | grep -q "noup.*set") + if [ $? -eq 0 ]; then + wlog $name "WARN" "Ceph 'noup' flag is set, aborting blocked ops check" + echo "false"; return + fi + + # Multiple OSD processes may be running, so we only run 'ceph osd tree' once + # as it takes some time to execute. This is initially cached from the hang + # check but check and call again here if needed + if [ -z "$CEPH_OSD_TREE" ]; then + execute_ceph_cmd CEPH_OSD_TREE $name "ceph osd tree" + if [ $? -ne 0 ]; then + wlog $name "WARN" "Ceph cmd exec failed, aborting blocked ops check" + echo "false"; return + fi + fi + + # Get osd status as 'up' or, for any other output, as 'down' + echo "$CEPH_OSD_TREE" | grep $name | grep -q "up" + if [ "$?" -eq 0 ]; then + # Look for and parse:'1 ops are blocked > 1048.58 sec on osd.1' + local blocked_time=$(echo "$CEPH_HEALTH_DETAIL" | grep $name | sed -rn 's/.*ops are blocked > ([[:digit:]]*).*/\1/p') + [[ "$blocked_time" == "" ]] && blocked_time=0 + if [ $blocked_time -gt $BLOCKED_OPS_RESTART_THRESH ]; then + wlog $name "WARN" "Detected blocked operations for $blocked_time seconds" + echo "true"; return + else + echo "false"; return + fi + fi +} + +osd_has_stuck_peering() { + local name=$1 + local id=$2 + + # Abort if we had previous errors with Ceph + if [ "$CEPH_FAILURE" = "true" ]; then + wlog $name "WARN" "Ceph cluster is marked as failed, aborting stuck peering check" + echo "false"; return + fi + + # Cache Ceph Health for later use as calling Ceph takes time This is + # initially cached from the hang check but check and call again here if + # needed + if [ -z "$CEPH_HEALTH_DETAIL" ]; then + execute_ceph_cmd CEPH_HEALTH_DETAIL $name "ceph health detail" + if [ $? -ne 0 ]; then + wlog $name "WARN" "Aborting stuck peering check" + echo "false"; return + fi + fi + + # Ignore health check if OSDs are administratively up + # Note this can be done with: 'ceph osd set nodown; /etc/init.d/ceph stop ' + $(echo "$CEPH_HEALTH_DETAIL" | grep -q "nodown.*set") + if [ $? -eq 0 ]; then + wlog $name "WARN" "Ceph 'nodown' flag is set, aborting stuck peering check" + echo "false"; return + fi + + + file="${DATA_PATH}/.${name}_stuck_peering_start" + max_blocked_time=0 + + $(echo "$CEPH_HEALTH_DETAIL" | grep "stuck peering" | awk '{split($0,a,"acting"); print a[2]}' | grep -q $id) + if [ "$?" -eq 0 ]; then + while read -r line; do + $(echo $line | awk '{split($0,a,"acting"); print a[2]}' | grep -q $id) + [ $? -eq 0 ] || continue + # Look for and parse:'pg 1.3b is stuck peering for 69.893513, current state peering, last acting [1,0]' + local blocked_time=$(echo "$line" | sed -rn 's/.*stuck peering for ([[:digit:]]*).*/\1/p') + if [ $blocked_time -gt $max_blocked_time ]; then + max_blocked_time="$blocked_time" + fi + done <<< "$(echo "$CEPH_HEALTH_DETAIL" | grep "stuck peering")" + fi + + + if [ $max_blocked_time -gt 0 ]; then + if [ -f ${file} ]; then + # check against old time + first_blocked_time=$(cat ${file}) + blocked_time=$((max_blocked_time - first_blocked_time)) + + # if stuck peering time restarted between status commands + # then allow it + if [ $blocked_time -lt 0 ]; then + blocked_time=$max_blocked_time + echo $max_blocked_time > ${file} + fi + + if [ $blocked_time -gt $STUCK_PEERING_RESTART_THRESH ]; then + wlog $name "WARN" "Detected stuck peering for $blocked_time seconds" + echo "true"; return + else + echo "false"; return + fi + else + # register the time for first detected stuck peering + echo $max_blocked_time > ${file} + fi + else + rm -f ${file} 2>/dev/null + fi + + +} + +###################### +#### StarlingX END ### +###################### + + +if [ `uname` = FreeBSD ]; then + GETOPT=/usr/local/bin/getopt +else + GETOPT=getopt +fi + +if id ceph > /dev/null 2>&1; then + SET_CEPHUSER_ARGS=" --setuser ceph --setgroup ceph" +fi + +usage_exit() { + echo "usage: $0 [options] {start|stop|restart|condrestart} [mon|osd|mds]..." + printf "Core options:\n" + printf "\t--allhosts / -a execute (via ssh) on all hosts in conf file\n" + printf "\t--cluster [cluster name] define the cluster name\n" + printf "\t--conf / -c [conf file] use [conf file] instead of default\n" + printf "\t--help / -h show this usage message\n" + printf "\t--hostname [hostname] override hostname lookup\n" + printf "\t-m [mon addr] mon address\n" + printf "\n" + printf "Other options:\n" + printf "\t--btrfs btrfs\n" + printf "\t--nobtrfs no btrfs\n" + printf "\t--btrfsumount btrfs umount\n" + printf "\t--fsmount fsmount\n" + printf "\t--nofsmount no fsmount\n" + printf "\t--fsumount fsumount\n" + printf "\t--restart restart on core dump\n" + printf "\t--norestart do not restart on core dump\n" + printf "\t--valgrind run via valgrind\n" + printf "\t--novalgrind do not run via valgrind\n" + printf "\t--verbose / -v be verbose\n" + exit +} + +# behave if we are not completely installed (e.g., Debian "removed, +# config remains" state) +test -f $LIBEXECDIR/ceph_common.sh || exit 0 + +. $LIBEXECDIR/ceph_common.sh + +EXIT_STATUS=0 + +signal_daemon() { + name=$1 + daemon=$2 + pidfile=$3 + signal=$4 + action=$5 + [ -z "$action" ] && action="Stopping" + printf "$action Ceph $name on $host..." + do_cmd "if [ -e $pidfile ]; then + pid=\`cat $pidfile\` + if ps -p \$pid -o args= | grep -q $daemon; then + cmd=\"kill $signal \$pid\" + printf \"\$cmd...\" + \$cmd + fi + fi" + echo done +} + +daemon_is_running() { + name=$1 + daemon=$2 + daemon_id=$3 + pidfile=$4 + do_cmd "[ -e $pidfile ] || exit 1 # no pid, presumably not running + pid=\`cat $pidfile\` + ps -p \$pid -o args= | grep $daemon | grep -qwe -i.$daemon_id && exit 0 # running + exit 1 # pid is something else" "" "okfail" +} + +stop_daemon() { + name=$1 + daemon=$2 + pidfile=$3 + signal=$4 + action=$5 + timeout=$6 + [ -z "$action" ] && action="Stopping" + printf "$action Ceph $name on $host..." + do_cmd "if [ -e $pidfile ] ; then + pid=\`cat $pidfile\` + timeout=$timeout + while ps -p \$pid -o args= | grep -q $daemon; do + if [ ! -z "$timeout" ]; then + if [ \$timeout -lt 0 ]; then + break + fi + timeout-=1 + fi + cmd=\"kill $signal \$pid\" + printf \"\$cmd...\" + \$cmd + sleep 1 + continue + done + fi" + echo done +} + +## command line options +options= + +OPTS=$(${GETOPT} -n 'init-ceph' -o 'hvam:c:' -l 'help,verbose,valgrind,novalgrind,allhosts,restart,norestart,btrfs,nobtrfs,fsmount,nofsmount,btrfsumount,fsumount,conf:,cluster:,hostname:' -- "$@") +if [ $? != 0 ] +then + exit 1 +fi + +eval set -- "$OPTS" + +dovalgrind= +docrun= +allhosts=0 +monaddr= +dofsmount=1 +dofsumount=0 +verbose=0 +use_default_conf=1 + +## set variables like cluster or conf +[ -e /etc/sysconfig/ceph ] && . /etc/sysconfig/ceph +[ -e /etc/default/ceph ] && . /etc/default/ceph + + +while echo $1 | grep -q '^-'; do # FIXME: why not '^-'? +case $1 in + -v | --verbose) + verbose=1 + ;; + --valgrind) + dovalgrind=1 + ;; + --novalgrind) + dovalgrind=0 + ;; + --allhosts | -a) + allhosts=1; + ;; + --restart) + docrun=1 + ;; + --norestart) + docrun=0 + ;; + -h | --help) + usage_exit + ;; + -m ) + [ -z "$2" ] && usage_exit + options="$options $1" + shift + MON_ADDR=$1 + ;; + --btrfs | --fsmount) + dofsmount=1 + ;; + --nobtrfs | --nofsmount) + dofsmount=0 + ;; + --btrfsumount | --fsumount) + dofsumount=1 + ;; + --conf | -c) + [ -z "$2" ] && usage_exit + options="$options $1" + shift + use_default_conf=0 + conf=$1 + ;; + --cluster ) + [ -z "$2" ] && usage_exit + options="$options $1" + shift + cluster=$1 + ;; + --hostname ) + [ -z "$2" ] && usage_exit + options="$options $1" + shift + hostname=$1 + ;; + -- ) + shift + break + ;; + *) + echo unrecognized option \'$1\' + usage_exit + ;; +esac +options="$options $1" +shift +done + +# if `--cluster` was not passed in, fallback to looking at the config name +if [ -z "$cluster" ]; then + cluster=`echo $conf | awk -F'/' '{print $(NF)}' | cut -d'.' -f 1` +else + # if we were told to use a given cluster name then $conf needs to be updated + # but just define it if `--conf` was not specified, otherwise we would be silently + # overriding $conf even if it was defined with `--conf` + if [ $use_default_conf -eq 1 ]; then + conf="/etc/ceph/$cluster.conf" + fi +fi + + +verify_conf + +command=$1 +[ -n "$*" ] && shift + +get_local_name_list +get_name_list "$@" + +# Reverse the order if we are stopping + +if [ "$command" = "stop" -o "$command" = "onestop" ]; then + for f in $what; do + new_order="$f $new_order" + done + what="$new_order" +fi + +# Check if the monitors are up before starting any mds +# This is needed only for Standard deployments + +. /etc/platform/platform.conf + +if [ "$system_type" == "Standard" ]; then + CEPH_STATUS='' + execute_ceph_cmd CEPH_STATUS "ceph status" "ceph -s" + if [ "$?" -ne 0 ]; then + what_out= + for name in $what; do + type=$(echo $name | cut -c 1-3) + if [ "$type" == "mds" ]; then + continue + fi + what_out+=" $name" + done + what=$what_out + fi +fi + +for name in $what; do + type=`echo $name | cut -c 1-3` # e.g. 'mon', if $item is 'mon1' + id=`echo $name | cut -c 4- | sed 's/^\\.//'` + num=$id + name="$type.$id" + + check_host $cluster || continue + + binary="$BINDIR/ceph-$type" + cmd="$binary -i $id" + if [ $ASSUME_DEV -eq 1 ]; then + cmd="PATH=$PWD:$PATH $cmd" + fi + + get_conf run_dir "/var/run/ceph" "run dir" + + get_conf pid_file "$run_dir/$type.$id.pid" "pid file" + + if [ "$command" = "start" -o "$command" = "onestart" ]; then + if [ -n "$pid_file" ]; then + do_cmd "mkdir -p "`dirname $pid_file` + cmd="$cmd --pid-file $pid_file" + fi + + get_conf log_dir "" "log dir" + [ -n "$log_dir" ] && do_cmd "mkdir -p $log_dir" + + get_conf auto_start "" "auto start" + if [ "$auto_start" = "no" ] || [ "$auto_start" = "false" ] || [ "$auto_start" = "0" ]; then + if [ -z "$@" ]; then + echo "Skipping Ceph $name on $host... auto start is disabled" + continue + fi + fi + + if daemon_is_running $name ceph-$type $id $pid_file; then + echo "Starting Ceph $name on $host...already running" + continue + fi + + get_conf copy_executable_to "" "copy executable to" + if [ -n "$copy_executable_to" ]; then + scp $binary "$host:$copy_executable_to" + binary="$copy_executable_to" + fi + fi + + # conf file + cmd="$cmd -c $conf" + + if echo $name | grep -q ^osd; then + get_conf osd_data "/var/lib/ceph/osd/$cluster-$id" "osd data" + get_conf fs_path "$osd_data" "fs path" # mount point defaults so osd data + get_conf fs_devs "" "devs" + if [ -z "$fs_devs" ]; then + # try to fallback to old keys + get_conf tmp_btrfs_devs "" "btrfs devs" + if [ -n "$tmp_btrfs_devs" ]; then + fs_devs="$tmp_btrfs_devs" + fi + fi + first_dev=`echo $fs_devs | cut '-d ' -f 1` + fi + + # do lockfile, if RH + get_conf lockfile "/var/lock/subsys/ceph" "lock file" + lockdir=`dirname $lockfile` + if [ ! -d "$lockdir" ]; then + lockfile="" + fi + + get_conf asok "$run_dir/$cluster-$type.$id.asok" "admin socket" + + case "$command" in + start|onestart) + # Increase max_open_files, if the configuration calls for it. + get_conf max_open_files "32768" "max open files" + # Remove stale admin socket + [ -n "$asok" ] && rm -f $asok + + # Wait for pending systemd core dumps + deadline=$(( $(date '+%s') + 300 )) + while [[ $(date '+%s') -lt "${deadline}" ]]; do + systemd_coredump_pid=$(pgrep -f "systemd-coredump.*ceph-${type}") + [[ -z "${systemd_coredump_pid}" ]] && break + wlog $name "INFO" "systemd-coredump ceph-${type} in progress: pid ${systemd_coredump_pid}" + sleep 1 + done + + # build final command + wrap="" + runmode="" + runarg="" + + [ -z "$docrun" ] && get_conf_bool docrun "0" "restart on core dump" + [ "$docrun" -eq 1 ] && wrap="$BINDIR/ceph-run" + + [ -z "$dovalgrind" ] && get_conf_bool valgrind "" "valgrind" + [ -n "$valgrind" ] && wrap="$wrap valgrind $valgrind" + + [ -n "$wrap" ] && runmode="-f &" && runarg="-f" + [ -n "$max_open_files" ] && files="ulimit -n $max_open_files;" + + [ -n "$TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES" ] && tcmalloc="TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES=$TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES" + + # StarlingX: start processes in scope under slice system-ceph.slice + # so that ceph processes do not start under this script's callers cgroup + if [ "$type" = "osd" ]; then + cmd="systemd-run --scope --unit=ceph-${type}-${id} --slice=system-ceph $cmd" + else + cmd="systemd-run --scope --unit=ceph-${type} --slice=system-ceph $cmd" + fi + + # StarlingX: not running as ceph user/group + cmd="$files $tcmalloc $wrap $cmd --cluster $cluster $runmode" + + if [ $dofsmount -eq 1 ] && [ -n "$fs_devs" ]; then + get_conf pre_mount "true" "pre mount command" + get_conf fs_type "" "osd mkfs type" + + if [ -z "$fs_type" ]; then + # try to fallback to old keys + get_conf tmp_devs "" "btrfs devs" + if [ -n "$tmp_devs" ]; then + fs_type="btrfs" + else + echo No filesystem type defined! + exit 0 + fi + fi + + get_conf fs_opt "" "osd mount options $fs_type" + if [ -z "$fs_opt" ]; then + if [ "$fs_type" = "btrfs" ]; then + #try to fallback to old keys + get_conf fs_opt "" "btrfs options" + fi + + if [ -z "$fs_opt" ]; then + if [ "$fs_type" = "xfs" ]; then + fs_opt="rw,noatime,inode64" + else + #fallback to use at least noatime + fs_opt="rw,noatime" + fi + fi + fi + + [ -n "$fs_opt" ] && fs_opt="-o $fs_opt" + [ -n "$pre_mount" ] && do_cmd "$pre_mount" + + do_root_cmd_okfail "mkdir -p $fs_path" + if [ "$fs_type" = "btrfs" ]; then + echo Mounting Btrfs on $host:$fs_path + do_root_cmd_okfail "modprobe btrfs ; btrfs device scan || btrfsctl -a ; egrep -q '^[^ ]+ $fs_path ' /proc/mounts && umount $fs_path ; mount -t btrfs $fs_opt $first_dev $fs_path" + else + echo Mounting $fs_type on $host:$fs_path + do_root_cmd_okfail "modprobe $fs_type ; egrep -q '^[^ ]+ $fs_path ' /proc/mounts && umount $fs_path ; mount -t $fs_type $fs_opt $first_dev $fs_path" + fi + if [ "$ERR" != "0" ]; then + EXIT_STATUS=$ERR + continue + fi + fi + + save_proc_startup_ok $name + + echo Starting Ceph $name on $host... + if [ ! -d $run_dir ]; then + # assume /var/run exists + install -d -m0770 -o ceph -g ceph /var/run/ceph + fi + get_conf pre_start_eval "" "pre start eval" + [ -n "$pre_start_eval" ] && $pre_start_eval + get_conf pre_start "" "pre start command" + get_conf post_start "" "post start command" + [ -n "$pre_start" ] && do_cmd "$pre_start" + do_cmd_okfail "$cmd" $runarg + if [ "$ERR" != "0" ]; then + EXIT_STATUS=$ERR + continue + fi + + . /etc/platform/platform.conf + if [ "${nodetype}" = "controller" ]; then + # StarlingX: Hook the transient services launched by systemd-run + # to allow for proper cleanup and orderly shutdown + + # Set nullglob so wildcards will return empty string if no match + shopt -s nullglob + + OSD_SERVICES=$(for svc in /run/systemd/system/ceph-osd*.service; do basename $svc; done | xargs echo) + for d in /run/systemd/system/ceph-osd*.d; do + cat < $d/starlingx-overrides.conf +[Unit] +Before=docker.service +After=sm-shutdown.service + +EOF + done + + for d in /run/systemd/system/ceph-mon*.d; do + cat < $d/starlingx-overrides.conf +[Unit] +Before=docker.service +After=sm-shutdown.service ${OSD_SERVICES} + +EOF + done + + shopt -u nullglob + systemctl daemon-reload + + fi + + [ -n "$post_start" ] && do_cmd "$post_start" + [ -n "$lockfile" ] && [ "$?" -eq 0 ] && touch $lockfile + ;; + + stop|onestop) + get_conf pre_stop "" "pre stop command" + get_conf post_stop "" "post stop command" + [ -n "$pre_stop" ] && do_cmd "$pre_stop" + + wlog $name "INFO" "Stopping process" + + if [ $(load_proc_state $name) != "$ST_HANGED" ]; then + stop_daemon $name ceph-$type $pid_file + else + # first try to gracefully close process, this should be fast if + # its threads still respond to the TERM signal + wlog $name "DEBUG" ">>> Sending term signal" + stop_daemon $name ceph-$type $pid_file TERM "" 5 + wlog $name "DEBUG" ">>> Sending kill signal" + # then just kill it + stop_daemon $name ceph-$type $pid_file KILL + fi + + [ -n "$pidfile" ] && rm -f $pidfile + [ -n "$asok" ] && rm -f $asok + [ -n "$post_stop" ] && do_cmd "$post_stop" + [ -n "$lockfile" ] && [ "$?" -eq 0 ] && rm -f $lockfile + # flush journal to data disk in background + if [ "$type" = "osd" ];then + $(/usr/bin/ceph-osd -i $id --flush-journal) & + fi + wlog $name "INFO" "Process stopped, setting state to $ST_STOPPED" + save_proc_state $name $ST_STOPPED + if [ $dofsumount -eq 1 ] && [ -n "$fs_devs" ]; then + echo Unmounting OSD volume on $host:$fs_path + do_root_cmd "umount $fs_path || true" + fi + ;; + + status) + if daemon_is_running $name ceph-$type $id $pid_file; then + + # ceph processes answer in around 100ms when the process works correctly + do_cmd "timeout 1 $BINDIR/ceph --admin-daemon $asok version 2>/dev/null || echo unknown" + + # log ceph osd state + if [ "$type" = "osd" ];then + CEPH_DAEMON_STATUS="" + execute_ceph_cmd CEPH_DAEMON_STATUS $name "ceph daemon $name status" + if [ $? -eq 0 ]; then + state=$(echo "$CEPH_DAEMON_STATUS" | awk -F ':' '/state/{gsub(/[,[:space:]]/, "", $2);print $2}') + if [ "$state" != "active" ]; then + wlog $name "INFO" "$name has the following state: $state" + fi + fi + fi + + # check if daemon is hung + is_hung=$(is_process_hung $name $type) + if [ "$is_hung" = "true" ]; then + echo "$name: hung." + # based on http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html + # exit codes from 150 to 199 are application specific, therefore we define one here + EXIT_STATUS=150 + else + # Wait a period of time prior to OSD start before restarting based on slow/blocked requests + if [ "$type" = "osd" ] && [ $BLOCKED_OPS_DETECTION_ENABLED = "true" ]; then + up_time=$(get_proc_run_time $name) + if [ $up_time -gt $BLOCKED_OPS_START_DETECTION ]; then + has_blocked_ops=$(osd_has_blocked_ops $name) + if [ "$has_blocked_ops" = "true" ]; then + echo "$name: blocked ops." + # based on http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html + # exit codes from 150 to 199 are application specific, therefore we define one here + EXIT_STATUS=151 + else + echo "$name: running." + fi + else + echo "$name: running." + fi + else + echo "$name: running." + fi + + # Wait a period of time prior to OSD start before restarting based on stuck peering + if [ "$type" = "osd" ] && [ $STUCK_PEERING_DETECTION_ENABLED = "true" ]; then + up_time=$(get_proc_run_time $name) + if [ $up_time -gt $STUCK_PEERING_START_DETECTION ]; then + has_stuck_peering=$(osd_has_stuck_peering $name $id) + if [ "$has_stuck_peering" = "true" ]; then + echo "$name: stuck peering." + # based on http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html + # exit codes from 150 to 199 are application specific, therefore we define one here + EXIT_STATUS=152 + else + echo "$name: running." + fi + else + echo "$name: running." + fi + else + echo "$name: running." + fi + fi + + elif [ -e "$pid_file" ]; then + # daemon is dead, but pid file still exists + echo "$name: dead." + EXIT_STATUS=1 + else + # daemon is dead, and pid file is gone + echo "$name: not running." + EXIT_STATUS=3 + fi + ;; + + ssh) + $ssh + ;; + + forcestop) + get_conf pre_forcestop "" "pre forcestop command" + get_conf post_forcestop "" "post forcestop command" + [ -n "$pre_forcestop" ] && do_cmd "$pre_forcestop" + stop_daemon $name ceph-$type $pid_file -9 + [ -n "$post_forcestop" ] && do_cmd "$post_forcestop" + [ -n "$lockfile" ] && [ "$?" -eq 0 ] && rm -f $lockfile + ;; + + killall) + echo "killall ceph-$type on $host" + do_cmd "pkill ^ceph-$type || true" + [ -n "$lockfile" ] && [ "$?" -eq 0 ] && rm -f $lockfile + ;; + + force-reload | reload) + signal_daemon $name ceph-$type $pid_file -1 "Reloading" + ;; + + restart|onerestart) + $0 $options stop $name + $0 $options start $name + ;; + + condrestart) + if daemon_is_running $name ceph-$type $id $pid_file; then + $0 $options stop $name + $0 $options start $name + else + echo "$name: not running." + fi + ;; + + cleanlogs) + echo removing logs + [ -n "$log_dir" ] && do_cmd "rm -f $log_dir/$type.$id.*" + ;; + + cleanalllogs) + echo removing all logs + [ -n "$log_dir" ] && do_cmd "rm -f $log_dir/* || true" + ;; + + *) + usage_exit + ;; + esac +done + +# wait for journal flushing to complete +if [ $command == "stop" ]; then + wait +fi + +exit $EXIT_STATUS diff --git a/ceph/ceph/debian/deb_folder/ceph-base.dirs b/ceph/ceph/debian/deb_folder/ceph-base.dirs new file mode 100644 index 000000000..cf605ad44 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.dirs @@ -0,0 +1,9 @@ +var/lib/ceph/bootstrap-mds +var/lib/ceph/bootstrap-mgr +var/lib/ceph/bootstrap-osd +var/lib/ceph/bootstrap-rbd +var/lib/ceph/bootstrap-rbd-mirror +var/lib/ceph/bootstrap-rgw +var/lib/ceph/crash +var/lib/ceph/crash/posted +var/lib/ceph/tmp diff --git a/ceph/ceph/debian/deb_folder/ceph-base.docs b/ceph/ceph/debian/deb_folder/ceph-base.docs new file mode 100644 index 000000000..b43bf86b5 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.docs @@ -0,0 +1 @@ +README.md diff --git a/ceph/ceph/debian/deb_folder/ceph-base.install b/ceph/ceph/debian/deb_folder/ceph-base.install new file mode 100644 index 000000000..9cecc1aa1 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.install @@ -0,0 +1,42 @@ +## install from source tree +lib/systemd/system/ceph-crash.service +usr/bin/ceph-crash +usr/bin/ceph-kvstore-tool +usr/bin/ceph-run +usr/bin/crushtool +usr/bin/monmaptool +usr/bin/osdmaptool +usr/lib/*/ceph/erasure-code/* +usr/lib/*/rados-classes/* +usr/lib/ceph/ceph_common.sh +usr/sbin/ceph-create-keys +usr/share/doc/ceph/sample.ceph.conf +usr/share/man/man8/ceph-create-keys.8 +usr/share/man/man8/ceph-deploy.8 +usr/share/man/man8/ceph-kvstore-tool.8 +usr/share/man/man8/ceph-run.8 +usr/share/man/man8/crushtool.8 +usr/share/man/man8/monmaptool.8 +usr/share/man/man8/osdmaptool.8 + +usr/bin/ceph-detect-init + +# if %{with stx} +etc/init.d/ceph +etc/init.d/mgr-restful-plugin +etc/init.d/ceph-init-wrapper +etc/ceph/ceph.conf.pmon +etc/ceph/ceph.conf +etc/services.d/* +usr/sbin/ceph-preshutdown.sh +lib/systemd/system/docker.service.d/starlingx-docker-override.conf +lib/systemd/system/ceph.service +lib/systemd/system/mgr-restful-plugin.service + +# if %{without stx} +# usr/libexec/systemd/system-preset/50-ceph.preset + +usr/sbin/ceph-disk + +usr/lib/python3/dist-packages/ceph_detect_init* +usr/lib/python3/dist-packages/ceph_disk* diff --git a/ceph/ceph/debian/deb_folder/ceph-base.lintian-overrides b/ceph/ceph/debian/deb_folder/ceph-base.lintian-overrides new file mode 100644 index 000000000..82dd042b4 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.lintian-overrides @@ -0,0 +1 @@ +package-has-unnecessary-activation-of-ldconfig-trigger diff --git a/ceph/ceph/debian/deb_folder/ceph-base.postinst b/ceph/ceph/debian/deb_folder/ceph-base.postinst new file mode 100644 index 000000000..23712a8b4 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.postinst @@ -0,0 +1,61 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# +# The current action is to simply remove the mistakenly-added +# /etc/init/ceph.conf file; this could be done in any of these cases, +# although technically it will leave the system in a different state +# than the original install that included that file. So instead we +# only remove on "configure", since that's the only time we know we're +# successful in installing a newer package than the erroneous version. + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in + configure) + rm -f /etc/init/ceph.conf + for DIR in `ls -1 /var/lib/ceph` ; do + if ! dpkg-statoverride --list /var/lib/ceph/$DIR >/dev/null; then + if [ -d /run/systemd/system ] && [ $DIR = 'mon' ]; then + # NOTE: upgrade file permissions for mon filesystem on + # systemd based installs only due to automatic + # restarting of ceph-mon daemon + chown -R $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR + else + chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR + fi + fi + done + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/ceph/ceph/debian/deb_folder/ceph-base.postrm b/ceph/ceph/debian/deb_folder/ceph-base.postrm new file mode 100644 index 000000000..05091b223 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-base.postrm @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +if [ "${1}" = "purge" ] ; then + rm -rf /var/log/ceph +fi + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-common.dirs b/ceph/ceph/debian/deb_folder/ceph-common.dirs new file mode 100644 index 000000000..ff05698c2 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.dirs @@ -0,0 +1,3 @@ +etc/ceph +var/lib/ceph +var/log/ceph diff --git a/ceph/ceph/debian/deb_folder/ceph-common.install b/ceph/ceph/debian/deb_folder/ceph-common.install new file mode 100755 index 000000000..a213c2f3a --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.install @@ -0,0 +1,52 @@ +#!/usr/bin/dh-exec --with=install +usr/share/bash-completion/completions/ceph +usr/share/bash-completion/completions/rados +usr/share/bash-completion/completions/radosgw-admin +usr/share/bash-completion/completions/rbd +lib/systemd/system/ceph.target + +# %if %{with stx} +etc/init.d/rbdmap + +# if %{without stx} +# lib/systemd/system/rbdmap.service + +etc/default/ceph +usr/bin/ceph +usr/bin/ceph-authtool +usr/bin/ceph-conf +usr/bin/ceph-dencoder +usr/bin/ceph-rbdnamer +usr/bin/ceph-syn +usr/bin/cephfs-data-scan +usr/bin/cephfs-journal-tool +usr/bin/cephfs-table-tool +usr/bin/rados +usr/bin/radosgw-admin +usr/bin/rbd +usr/bin/rbdmap +usr/bin/rbd-replay* +usr/bin/ceph-post-file +usr/sbin/mount.ceph sbin +usr/lib/*/ceph/compressor/* +usr/lib/*/ceph/crypto/* [amd64] +usr/share/man/man8/ceph-authtool.8 +usr/share/man/man8/ceph-conf.8 +usr/share/man/man8/ceph-dencoder.8 +usr/share/man/man8/ceph-rbdnamer.8 +usr/share/man/man8/ceph-syn.8 +usr/share/man/man8/ceph-post-file.8 +usr/share/man/man8/ceph.8 +usr/share/man/man8/mount.ceph.8 +usr/share/man/man8/rados.8 +usr/share/man/man8/radosgw-admin.8 +usr/share/man/man8/rbd.8 +usr/share/man/man8/rbdmap.8 +usr/share/man/man8/rbd-replay*.8 +usr/share/ceph/known_hosts_drop.ceph.com +usr/share/ceph/id_rsa_drop.ceph.com +usr/share/ceph/id_rsa_drop.ceph.com.pub +etc/ceph/rbdmap +lib/udev/rules.d/50-rbd.rules + + diff --git a/ceph/ceph/debian/deb_folder/ceph-common.lintian-overrides b/ceph/ceph/debian/deb_folder/ceph-common.lintian-overrides new file mode 100644 index 000000000..82dd042b4 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.lintian-overrides @@ -0,0 +1 @@ +package-has-unnecessary-activation-of-ldconfig-trigger diff --git a/ceph/ceph/debian/deb_folder/ceph-common.manpages b/ceph/ceph/debian/deb_folder/ceph-common.manpages new file mode 100644 index 000000000..643fa2a41 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.manpages @@ -0,0 +1 @@ +debian/man/ceph-crush-location.1 diff --git a/ceph/ceph/debian/deb_folder/ceph-common.postinst b/ceph/ceph/debian/deb_folder/ceph-common.postinst new file mode 100644 index 000000000..5535e9dee --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.postinst @@ -0,0 +1,140 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +# Let the admin override these distro-specified defaults. This is NOT +# recommended! +[ -f "/etc/default/ceph" ] && . /etc/default/ceph + +[ -z "$SERVER_HOME" ] && SERVER_HOME=/var/lib/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_NAME" ] && SERVER_NAME="Ceph storage service" +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph +[ -z "$SERVER_UID" ] && SERVER_UID=64045 # alloc by Debian base-passwd maintainer +[ -z "$SERVER_GID" ] && SERVER_GID=$SERVER_UID + + +# Groups that the user will be added to, if undefined, then none. +[ -z "$SERVER_ADDGROUP" ] && SERVER_ADDGROUP= + +# Custom dpkg-maintscript-helper type function to deal with +# nested /etc/default/ceph/ceph +finish_mv_ceph_defaults() { + rm -rf "/etc/default/ceph.dpkg-backup/ceph.dpkg-remove" + + [ -e "/etc/default/ceph.dpkg-backup/ceph" ] || return 0 + + echo "Preserving user changes to /etc/default/ceph (renamed from /etc/default/ceph/ceph)..." + if [ -f "/etc/default/ceph" ]; then + mv -f "/etc/default/ceph" "/etc/default/ceph.dpkg-new" + fi + mv -f "/etc/default/ceph.dpkg-backup/ceph" "/etc/default/ceph" +} + +case "$1" in + configure) + # create user to avoid running server as root + # 1. create group if not existing + if ! getent group | grep -q "^$SERVER_GROUP:" ; then + echo -n "Adding group $SERVER_GROUP.." + addgroup --quiet --system --gid $SERVER_GID \ + $SERVER_GROUP 2>/dev/null ||true + echo "..done" + fi + # 2. create user if not existing + if ! getent passwd | grep -q "^$SERVER_USER:"; then + echo -n "Adding system user $SERVER_USER.." + adduser --quiet \ + --system \ + --no-create-home \ + --disabled-password \ + --uid $SERVER_UID \ + --gid $SERVER_GID \ + --home $SERVER_HOME \ + $SERVER_USER 2>/dev/null || true + echo "..done" + fi + # 3. adjust passwd entry + echo -n "Setting system user $SERVER_USER properties.." + usermod -c "$SERVER_NAME" \ + -d $SERVER_HOME \ + -g $SERVER_GROUP \ + $SERVER_USER + + # Unlock $SERVER_USER in case it is locked from an uninstall + if [ -f /etc/shadow ]; then + usermod -U -e '' $SERVER_USER + else + usermod -U $SERVER_USER + fi + echo "..done" + + # 5. adjust file and directory permissions + if ! dpkg-statoverride --list $SERVER_HOME >/dev/null + then + chown $SERVER_USER:$SERVER_GROUP $SERVER_HOME + chmod u=rwx,g=rx,o= $SERVER_HOME + fi + if ! dpkg-statoverride --list /var/log/ceph >/dev/null + then + chown -R $SERVER_USER:$SERVER_GROUP /var/log/ceph + # members of group ceph can log here, but cannot remove + # others' files. non-members cannot read any logs. + chmod u=rwx,g=rwxs,o=t /var/log/ceph + fi + + # 6. fix /var/run/ceph + if [ -d /var/run/ceph ]; then + echo -n "Fixing /var/run/ceph ownership.." + chown $SERVER_USER:$SERVER_GROUP /var/run/ceph + echo "..done" + fi + + # create /run/ceph. fail softly if systemd isn't present or + # something. + [ -x /bin/systemd-tmpfiles ] && systemd-tmpfiles --create || true + + # Complete renames of /etc/default/ceph + if [ -n "$2" ] && + dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then + finish_mv_ceph_defaults + # Preserve dpkg-backup directory if it still contains + # any file + if ! ls -1qA "/etc/default/ceph.dpkg-backup" | grep -q . ; then + rm -rf "/etc/default/ceph.dpkg-backup" + fi + fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/ceph/ceph/debian/deb_folder/ceph-common.postrm b/ceph/ceph/debian/deb_folder/ceph-common.postrm new file mode 100644 index 000000000..e4051180d --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.postrm @@ -0,0 +1,74 @@ +#!/bin/sh +# postrm script for ceph-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +# Custom dpkg-maintscript-helper type function to deal with +# nested /etc/default/ceph/ceph +abort_mv_ceph_defaults() { + if [ -e "/etc/default/ceph.dpkg-backup/ceph.dpkg-remove" ]; then + echo "Reinstalling /etc/default/ceph/ceph that was moved away" + mv "/etc/default/ceph.dpkg-backup" "/etc/default/ceph" + mv "/etc/default/ceph/ceph.dpkg-remove" "/etc/default/ceph/ceph" + fi +} + +case "$1" in + remove) + ;; + + purge) + [ -f "/etc/default/ceph" ] && . /etc/default/ceph + [ -z "$SERVER_USER" ] && SERVER_USER=ceph + + rm -rf /var/log/ceph + rm -rf /etc/ceph + + if [ -f /etc/shadow ]; then + usermod -L -e 1 $SERVER_USER + else + usermod -L $SERVER_USER + fi + + ;; + + abort-install|abort-upgrade) + if [ -n "$2" ] && + dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then + abort_mv_ceph_defaults + fi + ;; + + upgrade|failed-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-common.preinst b/ceph/ceph/debian/deb_folder/ceph-common.preinst new file mode 100644 index 000000000..ef14f1eb1 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.preinst @@ -0,0 +1,29 @@ +#!/bin/sh + +set -e + +# Custom dpkg-maintscript-helper type function to deal with +# nested /etc/default/ceph/ceph +prepare_mv_ceph_defaults() { + local md5sum old_md5sum + md5sum="$(md5sum "/etc/default/ceph/ceph" | sed -e 's/ .*//')" + old_md5sum="$(dpkg-query -W -f='${Conffiles}' "ceph-common" | \ + sed -n -e "\'^ /etc/default/ceph/ceph ' { s/ obsolete$//; s/.* //; p }")" + if [ "$md5sum" = "$old_md5sum" ]; then + mv -f "/etc/default/ceph/ceph" "/etc/default/ceph/ceph.dpkg-remove" + mv -f "/etc/default/ceph" "/etc/default/ceph.dpkg-backup" + fi +} + +case "$1" in + upgrade|install) + if [ -d /etc/default/ceph ] && [ -n "$2" ] && + dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then + prepare_mv_ceph_defaults + fi + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/ceph/ceph/debian/deb_folder/ceph-common.rbdmap.init b/ceph/ceph/debian/deb_folder/ceph-common.rbdmap.init new file mode 100755 index 000000000..6058e397e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-common.rbdmap.init @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# +# rbdmap Ceph RBD Mapping +# +# chkconfig: 2345 20 80 +# description: Ceph RBD Mapping + +### BEGIN INIT INFO +# Provides: rbdmap +# Required-Start: $network $remote_fs +# Required-Stop: $network $remote_fs +# Should-Start: ceph +# Should-Stop: ceph +# X-Start-Before: $x-display-manager +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Ceph RBD Mapping +# Description: Ceph RBD Mapping +### END INIT INFO + +RBDMAPFILE="/etc/ceph/rbdmap" + +if [ -e /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions +fi + + + + +case "$1" in + start) + rbdmap device map + ;; + + stop) + rbdmap device unmap + ;; + + restart|force-reload) + $0 stop + $0 start + ;; + + reload) + rbdmap device map + ;; + + status) + rbd device list + ;; + + *) + echo "Usage: rbdmap {start|stop|restart|force-reload|reload|status}" + exit 1 + ;; +esac diff --git a/ceph/ceph/debian/deb_folder/ceph-fs-common.install b/ceph/ceph/debian/deb_folder/ceph-fs-common.install new file mode 100644 index 000000000..a4f0bab50 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-fs-common.install @@ -0,0 +1,4 @@ +usr/bin/cephfs +usr/sbin/mount.ceph sbin +usr/share/man/man8/cephfs.8 +usr/share/man/man8/mount.ceph.8 diff --git a/ceph/ceph/debian/deb_folder/ceph-fuse.install b/ceph/ceph/debian/deb_folder/ceph-fuse.install new file mode 100644 index 000000000..e132b7957 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-fuse.install @@ -0,0 +1,4 @@ +lib/systemd/system/ceph-fuse* +usr/bin/ceph-fuse +usr/sbin/mount.fuse.ceph sbin +usr/share/man/man8/ceph-fuse.8 diff --git a/ceph/ceph/debian/deb_folder/ceph-fuse.manpages b/ceph/ceph/debian/deb_folder/ceph-fuse.manpages new file mode 100644 index 000000000..e4c9c231e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-fuse.manpages @@ -0,0 +1 @@ +debian/man/mount.fuse.ceph.8 diff --git a/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.dirs b/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.dirs new file mode 100644 index 000000000..b29a93e2d --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.dirs @@ -0,0 +1,3 @@ +etc/grafana/dashboards/ceph-dashboard +etc/grafana/dashboards +etc/grafana diff --git a/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.install b/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.install new file mode 100644 index 000000000..9591d1bf6 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-grafana-dashboards.install @@ -0,0 +1,3 @@ +etc/grafana/dashboards/ceph-dashboard/* +monitoring/grafana/dashboards/README +monitoring/grafana/README.md diff --git a/ceph/ceph/debian/deb_folder/ceph-mds.dirs b/ceph/ceph/debian/deb_folder/ceph-mds.dirs new file mode 100644 index 000000000..984526808 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mds.dirs @@ -0,0 +1 @@ +var/lib/ceph/mds diff --git a/ceph/ceph/debian/deb_folder/ceph-mds.install b/ceph/ceph/debian/deb_folder/ceph-mds.install new file mode 100644 index 000000000..a26d5596c --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mds.install @@ -0,0 +1,3 @@ +lib/systemd/system/ceph-mds* +usr/bin/ceph-mds +usr/share/man/man8/ceph-mds.8 diff --git a/ceph/ceph/debian/deb_folder/ceph-mds.postinst b/ceph/ceph/debian/deb_folder/ceph-mds.postinst new file mode 100644 index 000000000..3ae908a99 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mds.postinst @@ -0,0 +1,47 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mds +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in + configure) + if ! dpkg-statoverride --list /var/lib/ceph/mds >/dev/null; + then + chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mds + fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.install b/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.install new file mode 100644 index 000000000..8d3c8bd17 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/dashboard diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.postinst new file mode 100644 index 000000000..e681ef6b6 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-dashboard.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-dashboard +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.install b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.install new file mode 100644 index 000000000..58481b253 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/diskprediction_cloud diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.postinst new file mode 100644 index 000000000..d8e7a50ed --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-cloud.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-diskprediction-cloud +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.install b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.install new file mode 100644 index 000000000..a381e251a --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/diskprediction_local diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.postinst new file mode 100644 index 000000000..a3293a88e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-diskprediction-local.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-diskprediction-local +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.install b/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.install new file mode 100644 index 000000000..734da94ca --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/k8sevents diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.postinst new file mode 100644 index 000000000..4ab578fd1 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-k8sevents.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-k8sevents +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-rook.install b/ceph/ceph/debian/deb_folder/ceph-mgr-rook.install new file mode 100644 index 000000000..50cadb435 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-rook.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/rook diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-rook.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-rook.postinst new file mode 100644 index 000000000..a3293a88e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-rook.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-diskprediction-local +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.install b/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.install new file mode 100644 index 000000000..4023e4ead --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.install @@ -0,0 +1 @@ +usr/share/ceph/mgr/ssh diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.postinst new file mode 100644 index 000000000..c0e3b13c5 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr-ssh.postinst @@ -0,0 +1,43 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr-ssh +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + # attempt to load the plugin if the mgr is running + deb-systemd-invoke try-restart ceph-mgr.target + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr.dirs b/ceph/ceph/debian/deb_folder/ceph-mgr.dirs new file mode 100644 index 000000000..636b3cf4a --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr.dirs @@ -0,0 +1 @@ +var/lib/ceph/mgr diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr.install b/ceph/ceph/debian/deb_folder/ceph-mgr.install new file mode 100644 index 000000000..cf3cfae46 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr.install @@ -0,0 +1,32 @@ +lib/systemd/system/ceph-mgr* +usr/bin/ceph-mgr +usr/share/ceph/mgr/alerts +usr/share/ceph/mgr/ansible +usr/share/ceph/mgr/balancer +usr/share/ceph/mgr/crash +usr/share/ceph/mgr/deepsea +usr/share/ceph/mgr/devicehealth + +# Not included in RPM. +# usr/share/ceph/mgr/influx + +usr/share/ceph/mgr/insights +usr/share/ceph/mgr/iostat +usr/share/ceph/mgr/localpool +usr/share/ceph/mgr/mgr_module.* +usr/share/ceph/mgr/mgr_util.* +usr/share/ceph/mgr/orchestrator.* +usr/share/ceph/mgr/orchestrator_cli +usr/share/ceph/mgr/osd_perf_query +usr/share/ceph/mgr/pg_autoscaler +usr/share/ceph/mgr/progress +usr/share/ceph/mgr/prometheus +usr/share/ceph/mgr/rbd_support +usr/share/ceph/mgr/restful +usr/share/ceph/mgr/selftest +usr/share/ceph/mgr/status +usr/share/ceph/mgr/telegraf +usr/share/ceph/mgr/telemetry +usr/share/ceph/mgr/test_orchestrator +usr/share/ceph/mgr/volumes +usr/share/ceph/mgr/zabbix diff --git a/ceph/ceph/debian/deb_folder/ceph-mgr.postinst b/ceph/ceph/debian/deb_folder/ceph-mgr.postinst new file mode 100644 index 000000000..6d38ccf09 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mgr.postinst @@ -0,0 +1,51 @@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mgr +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in + configure) + [ -x /sbin/start ] && start ceph-mgr-all || : + + if ! dpkg-statoverride --list /var/lib/ceph/mgr >/dev/null + then + chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mgr + fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-mon.dirs b/ceph/ceph/debian/deb_folder/ceph-mon.dirs new file mode 100644 index 000000000..e2845f602 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mon.dirs @@ -0,0 +1 @@ +var/lib/ceph/mon diff --git a/ceph/ceph/debian/deb_folder/ceph-mon.install b/ceph/ceph/debian/deb_folder/ceph-mon.install new file mode 100644 index 000000000..70a837201 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mon.install @@ -0,0 +1,7 @@ +# %if %{with stx} +# %exclude %{_unitdir}/ceph-mon* +# lib/systemd/system/ceph-mon* + +usr/bin/ceph-mon +usr/bin/ceph-monstore-tool +usr/share/man/man8/ceph-mon.8 diff --git a/ceph/ceph/debian/deb_folder/ceph-mon.postinst b/ceph/ceph/debian/deb_folder/ceph-mon.postinst new file mode 100644 index 000000000..454ffc141 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-mon.postinst @@ -0,0 +1,46 @@ +#!/bin/bash +# vim: set noet ts=8: +# postinst script for ceph-mon +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in + configure) + : + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/ceph-osd.dirs b/ceph/ceph/debian/deb_folder/ceph-osd.dirs new file mode 100644 index 000000000..36b99177e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-osd.dirs @@ -0,0 +1,2 @@ +var/lib/ceph/osd +lib/udev/rules.d diff --git a/ceph/ceph/debian/deb_folder/ceph-osd.install b/ceph/ceph/debian/deb_folder/ceph-osd.install new file mode 100644 index 000000000..1d1eab9fb --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-osd.install @@ -0,0 +1,31 @@ +debian/udev/* lib/udev/rules.d +etc/sudoers.d/ceph-osd-smartctl +etc/sysctl.d/30-ceph-osd.conf + +# %if %{without stx} +# lib/systemd/system/ceph-osd* +# lib/systemd/system/ceph-volume@.service + +usr/bin/ceph-bluestore-tool +usr/bin/ceph-clsinfo +usr/bin/ceph-objectstore-tool +usr/bin/ceph-osd +usr/bin/ceph-osdomap-tool +usr/lib/ceph/ceph-osd-prestart.sh +usr/lib/python*/dist-packages/ceph_volume-* +usr/lib/python*/dist-packages/ceph_volume/* +usr/sbin/ceph-volume +usr/sbin/ceph-volume-systemd +usr/share/man/man8/ceph-bluestore-tool.8 +usr/share/man/man8/ceph-clsinfo.8 +usr/share/man/man8/ceph-osd.8 +usr/share/man/man8/ceph-volume-systemd.8 +usr/share/man/man8/ceph-volume.8 + +# if %{with stx} +usr/sbin/ceph-manage-journal + +lib/udev/rules.d/60-ceph-by-parttypeuuid.rules + +# %if %{without stx} +# lib/udev/rules.d/95-ceph-osd.rules diff --git a/ceph/ceph/debian/deb_folder/ceph-resource-agents.install b/ceph/ceph/debian/deb_folder/ceph-resource-agents.install new file mode 100644 index 000000000..30843f62f --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph-resource-agents.install @@ -0,0 +1 @@ +usr/lib/ocf/resource.d/ceph/* diff --git a/ceph/ceph/debian/deb_folder/ceph.NEWS b/ceph/ceph/debian/deb_folder/ceph.NEWS new file mode 100644 index 000000000..ee9db2f22 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph.NEWS @@ -0,0 +1,180 @@ +ceph (10.2.5-1) unstable; urgency=medium + + ## Upgrades from Debian Jessie + + Online upgrades from Ceph versions prior to Hammer (0.94.x) are not + supported by upstream. As Debian Jessie has Ceph Firefly (0.80.x) an + online upgrade from Jessie to Stretch is not possible. You have to first + shutdown all Ceph daemons on all nodes, upgrade everything to the new + version and start all daemons again. + + Ceph daemons are not automatically restarted on upgrade to minimize + disruption. You have to manually restart them after the upgrade. + + -- Gaudenz Steinlin Sun, 08 Jan 2017 14:57:35 +0100 + +ceph (9.2.0-1) experimental; urgency=medium + + ## systemd Enablement + + For all distributions that support systemd (Debian Jessie 8.x, + Ubuntu >= 16.04), Ceph daemons are now managed using upstream provided + systemd files instead of the legacy sysvinit scripts or distro provided + systemd files. For example: + + systemctl start ceph.target # start all daemons + systemctl status ceph-osd@12 # check status of osd.12 + + To upgrade existing deployments that use the older systemd service + configurations (Ubuntu >= 15.04, Debian >= Jessie), you need to switch + to using the new ceph-mon@ service: + + systemctl stop ceph-mon + systemctl disable ceph-mon + + systemctl start ceph-mon@`hostname` + systemctl enable ceph-mon@`hostname` + + and also enable the ceph target post upgrade: + + systemctl enable ceph.target + + The main notable distro that is *not* using systemd is Ubuntu 14.04 + (The next Ubuntu LTS, 16.04, will use systemd instead of upstart). + + ## Ceph daemons no longer run as root + + Ceph daemons now run as user and group 'ceph' by default. The + ceph user has a static UID assigned by Debian to ensure consistency + across servers within a Ceph deployment. + + If your systems already have a ceph user, upgrading the package will cause + problems. We suggest you first remove or rename the existing 'ceph' user + and 'ceph' group before upgrading. + + When upgrading, administrators have two options: + + 1. Add the following line to 'ceph.conf' on all hosts: + + setuser match path = /var/lib/ceph/$type/$cluster-$id + + This will make the Ceph daemons run as root (i.e., not drop + privileges and switch to user ceph) if the daemon's data + directory is still owned by root. Newly deployed daemons will + be created with data owned by user ceph and will run with + reduced privileges, but upgraded daemons will continue to run as + root. + + 2. Fix the data ownership during the upgrade. This is the + preferred option, but it is more work and can be very time + consuming. The process for each host is to: + + 1. Upgrade the ceph package. This creates the ceph user and group. For + example: + + apt-get install ceph + + NOTE: the permissions on /var/lib/ceph/mon will be set to ceph:ceph + as part of the package upgrade process on existing *systemd* + based installations; the ceph-mon systemd service will be + automatically restarted as part of the upgrade. All other + filesystem permissions on systemd based installs will + remain unmodified by the upgrade. + + 2. Stop the daemon(s): + + systemctl stop ceph-osd@* # debian, ubuntu >= 15.04 + stop ceph-all # ubuntu 14.04 + + 3. Fix the ownership: + + chown -R ceph:ceph /var/lib/ceph + + 4. Restart the daemon(s): + + start ceph-all # ubuntu 14.04 + systemctl start ceph.target # debian, ubuntu >= 15.04 + + Alternatively, the same process can be done with a single daemon + type, for example by stopping only monitors and chowning only + '/var/lib/ceph/osd'. + + ## KeyValueStore OSD on-disk format changes + + The on-disk format for the experimental KeyValueStore OSD backend has + changed. You will need to remove any OSDs using that backend before you + upgrade any test clusters that use it. + + ## Deprecated commands + + 'ceph scrub', 'ceph compact' and 'ceph sync force' are now DEPRECATED. + Users should instead use 'ceph mon scrub', 'ceph mon compact' and + 'ceph mon sync force'. + + ## Full pool behaviour + + When a pool quota is reached, librados operations now block indefinitely, + the same way they do when the cluster fills up. (Previously they would + return -ENOSPC). By default, a full cluster or pool will now block. If + your librados application can handle ENOSPC or EDQUOT errors gracefully, + you can get error returns instead by using the new librados + OPERATION_FULL_TRY flag. + + -- James Page Mon, 30 Nov 2015 09:23:09 +0000 + +ceph (0.80.9-2) unstable; urgency=medium + + ## CRUSH fixes in 0.80.9 + + The 0.80.9 point release fixes several issues with CRUSH that trigger excessive + data migration when adjusting OSD weights. These are most obvious when a very + small weight change (e.g., a change from 0 to .01) triggers a large amount of + movement, but the same set of bugs can also lead to excessive (though less + noticeable) movement in other cases. + + However, because the bug may already have affected your cluster, fixing it + may trigger movement back to the more correct location. For this reason, you + must manually opt-in to the fixed behavior. + + In order to set the new tunable to correct the behavior: + + ceph osd crush set-tunable straw_calc_version 1 + + Note that this change will have no immediate effect. However, from this + point forward, any ‘straw’ bucket in your CRUSH map that is adjusted will get + non-buggy internal weights, and that transition may trigger some rebalancing. + + You can estimate how much rebalancing will eventually be necessary on your + cluster with: + + ceph osd getcrushmap -o /tmp/cm + crushtool -i /tmp/cm --num-rep 3 --test --show-mappings > /tmp/a 2>&1 + crushtool -i /tmp/cm --set-straw-calc-version 1 -o /tmp/cm2 + crushtool -i /tmp/cm2 --reweight -o /tmp/cm2 + crushtool -i /tmp/cm2 --num-rep 3 --test --show-mappings > /tmp/b 2>&1 + wc -l /tmp/a # num total mappings + diff -u /tmp/a /tmp/b | grep -c ^+ # num changed mappings + + Divide the total number of lines in /tmp/a with the number of lines + changed. We've found that most clusters are under 10%. + + You can force all of this rebalancing to happen at once with: + + ceph osd crush reweight-all + + Otherwise, it will happen at some unknown point in the future when + CRUSH weights are next adjusted. + + ## Mapping rbd devices with rbdmap on systemd systems + + If you have setup rbd mappings in /etc/ceph/rbdmap and corresponding mounts + in /etc/fstab things might break with systemd because systemd waits for the + rbd device to appear before the legacy rbdmap init file has a chance to run + and drops into emergency mode if it times out. + + This can be fixed by adding the nofail option in /etc/fstab to all rbd + backed mount points. With this systemd does not wait for the device and + proceeds with the boot process. After rbdmap mapped the device, systemd + detects the new device and mounts the file system. + + -- Gaudenz Steinlin Mon, 04 May 2015 22:49:48 +0200 diff --git a/ceph/ceph/debian/deb_folder/ceph.lintian-overrides b/ceph/ceph/debian/deb_folder/ceph.lintian-overrides new file mode 100644 index 000000000..99b3db84b --- /dev/null +++ b/ceph/ceph/debian/deb_folder/ceph.lintian-overrides @@ -0,0 +1 @@ +empty-binary-package diff --git a/ceph/ceph/debian/deb_folder/cephfs-shell.install b/ceph/ceph/debian/deb_folder/cephfs-shell.install new file mode 100644 index 000000000..4713a81b1 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/cephfs-shell.install @@ -0,0 +1,2 @@ +usr/bin/cephfs-shell +usr/lib/python3*/dist-packages/cephfs_shell-*.egg-info diff --git a/ceph/ceph/debian/deb_folder/changelog b/ceph/ceph/debian/deb_folder/changelog new file mode 100644 index 000000000..d0c4ae8f7 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/changelog @@ -0,0 +1,883 @@ +ceph (14.2.22-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 29 Jun 2021 22:09:07 +0000 + +ceph (14.2.21-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 13 May 2021 17:23:05 +0000 + +ceph (14.2.20-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 19 Apr 2021 14:11:13 +0000 + +ceph (14.2.19-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 30 Mar 2021 16:19:15 +0000 + +ceph (14.2.18-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 15 Mar 2021 17:46:19 +0000 + +ceph (14.2.17-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 11 Mar 2021 17:07:30 +0000 + +ceph (14.2.16-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Wed, 16 Dec 2020 17:34:57 +0000 + +ceph (14.2.15-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 23 Nov 2020 18:30:13 +0000 + +ceph (14.2.14-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 17 Nov 2020 18:10:08 +0000 + +ceph (14.2.13-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 30 Oct 2020 14:54:35 +0000 + +ceph (14.2.12-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 19 Oct 2020 20:19:19 +0000 + +ceph (14.2.11-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 10 Aug 2020 20:15:20 +0000 + +ceph (14.2.10-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 25 Jun 2020 17:32:29 +0000 + +ceph (14.2.9-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 09 Apr 2020 16:17:27 +0000 + +ceph (14.2.8-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 02 Mar 2020 17:49:19 +0000 + +ceph (14.2.7-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 31 Jan 2020 17:07:50 +0000 + +ceph (14.2.6-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Wed, 08 Jan 2020 18:36:52 +0000 + +ceph (14.2.5-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 06 Dec 2019 16:42:32 +0000 + +ceph (14.2.4-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 13 Sep 2019 14:07:41 -0400 + +ceph (14.2.3-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 03 Sep 2019 13:19:56 +0000 + +ceph (14.2.2-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Wed, 17 Jul 2019 15:12:34 +0000 + +ceph (14.2.1-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 25 Apr 2019 18:15:46 +0000 + +ceph (14.2.0-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 18 Mar 2019 10:08:27 +0000 + +ceph (14.1.1-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 11 Mar 2019 16:42:54 +0000 + +ceph (14.1.0-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 22 Feb 2019 18:07:06 +0000 + +ceph (13.1.0-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 03 May 2018 17:57:32 +0000 + +ceph (12.1.2-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 01 Aug 2017 17:55:37 +0000 + +ceph (12.1.1-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Mon, 17 Jul 2017 16:55:59 +0000 + +ceph (12.1.0-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 22 Jun 2017 15:43:47 +0000 + +ceph (12.0.3-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Tue, 16 May 2017 12:42:53 +0000 + +ceph (12.0.2-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Thu, 20 Apr 2017 19:59:57 +0000 + +ceph (12.0.1-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Fri, 24 Mar 2017 15:47:57 +0000 + +ceph (12.0.0-1) stable; urgency=medium + + * New upstream release + + -- Ceph Release Team Wed, 08 Feb 2017 13:57:30 +0000 + +ceph (11.1.0-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Mon, 12 Dec 2016 18:27:51 +0000 + +ceph (11.0.2-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Mon, 17 Oct 2016 11:16:49 +0000 + +ceph (11.0.1-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Tue, 11 Oct 2016 16:27:56 +0000 + +ceph (11.0.0-1) stable; urgency=low + + * New upstream release + + -- Sage Weil Tue, 28 Jun 2016 11:41:16 -0400 + +ceph (10.2.0-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Wed, 20 Apr 2016 11:29:47 +0000 + +ceph (10.1.2-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Tue, 12 Apr 2016 17:42:55 +0000 + +ceph (10.1.1-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Wed, 06 Apr 2016 00:45:18 +0000 + +ceph (10.1.0-1) stable; urgency=medium + + * New upstream release + + -- Alfredo Deza Thu, 24 Mar 2016 10:53:47 +0000 + +ceph (10.0.5) stable; urgency=low + + * New upstream release (just fixing changelog) + + -- Sage Weil Fri, 11 Mar 2016 12:04:26 -0500 + +ceph (10.0.4) stable; urgency=low + + * New upstream release + + -- Sage Weil Thu, 03 Mar 2016 13:34:18 -0500 + +ceph (10.0.3) stable; urgency=low + + * New upstream release + + -- Sage Weil Mon, 08 Feb 2016 17:10:25 -0500 + +ceph (10.0.2-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Wed, 13 Jan 2016 16:22:26 +0000 + +ceph (10.0.1-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Mon, 14 Dec 2015 23:48:54 +0000 + +ceph (10.0.0-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Mon, 16 Nov 2015 21:41:53 +0000 + +ceph (9.2.0-1) stable; urgency=low + + * New upstream release + + -- Jenkins Build Slave User Tue, 03 Nov 2015 16:58:32 +0000 + +ceph (9.1.0-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 13 Oct 2015 05:56:36 -0700 + +ceph (9.0.3-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Fri, 21 Aug 2015 12:46:31 -0700 + +ceph (9.0.2-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 14 Jul 2015 13:10:31 -0700 + +ceph (9.0.1-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Fri, 05 Jun 2015 10:59:02 -0700 + +ceph (9.0.0-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Mon, 04 May 2015 12:32:58 -0700 + +ceph (0.94-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 07 Apr 2015 10:05:40 -0700 + +ceph (0.93-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Fri, 27 Feb 2015 09:52:53 -0800 + +ceph (0.92-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Mon, 02 Feb 2015 10:35:27 -0800 + +ceph (0.91-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 13 Jan 2015 12:10:22 -0800 + +ceph (0.90-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Fri, 19 Dec 2014 06:56:22 -0800 + +ceph (0.89-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Wed, 03 Dec 2014 08:18:33 -0800 + +ceph (0.88-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 11 Nov 2014 09:33:12 -0800 + +ceph (0.87-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Wed, 29 Oct 2014 11:03:55 -0700 + +ceph (0.86-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 07 Oct 2014 06:20:21 -0700 + +ceph (0.85-1) stable; urgency=low + + * Development release + + -- Alfredo Deza Mon, 08 Sep 2014 06:31:31 -0700 + +ceph (0.84-1) stable; urgency=low + + * Development release + + -- Alfredo Deza Mon, 18 Aug 2014 09:02:20 -0700 + +ceph (0.83-1) stable; urgency=low + + * Development release + + -- Alfredo Deza Tue, 29 Jul 2014 13:42:53 -0700 + +ceph (0.82-1) stable; urgency=low + + * Development release + + -- Alfredo Deza Wed, 25 Jun 2014 16:47:51 +0000 + +ceph (0.81-1) stable; urgency=low + + * Development release + + -- Alfredo Deza Mon, 02 Jun 2014 18:37:27 +0000 + +ceph (0.80-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 06 May 2014 14:03:27 +0000 + +ceph (0.80-rc1-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Tue, 22 Apr 2014 21:21:44 +0000 + +ceph (0.79-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Mon, 07 Apr 2014 16:48:36 +0000 + +ceph (0.78-1) stable; urgency=low + + * New upstream release + + -- Alfredo Deza Fri, 21 Mar 2014 22:05:12 +0000 + +ceph (0.77-1) stable; urgency=low + + * New upstream release + + -- Ken Dreyer Wed, 19 Feb 2014 22:54:06 +0000 + +ceph (0.76-1) stable; urgency=low + + * New upstream release + + -- Ken Dreyer Mon, 03 Feb 2014 18:14:59 +0000 + +ceph (0.75-1) stable; urgency=low + + * New upstream release + + -- Ken Dreyer Mon, 13 Jan 2014 21:05:07 +0000 + +ceph (0.74-1) stable; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 30 Dec 2013 21:02:35 +0000 + +ceph (0.73-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 10 Dec 2013 04:55:06 +0000 + +ceph (0.72-1) stable; urgency=low + + * New upstream release + + -- Gary Lowell Thu, 07 Nov 2013 20:25:18 +0000 + +ceph (0.72-rc1-1) stable; urgency=low + + * New upstream release + + -- Gary Lowell Wed, 30 Oct 2013 00:44:25 +0000 + +ceph (0.71-1) stable; urgency=low + + * New upstream release + + -- Gary Lowell Thu, 17 Oct 2013 09:19:02 +0000 + +ceph (0.70-1) stable; urgency=low + + * New upstream release + + -- Gary Lowell Fri, 04 Oct 2013 20:11:51 +0000 + +ceph (0.69-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Wed, 18 Sep 2013 01:39:47 +0000 + +ceph (0.68-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 03 Sep 2013 16:10:11 -0700 + +ceph (0.67-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 13 Aug 2013 10:44:30 -0700 + +ceph (0.67-rc3-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 30 Jul 2013 14:37:40 -0700 + +ceph (0.67-rc2-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Wed, 24 Jul 2013 16:18:33 -0700 + +ceph (0.67-rc1-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 22 Jul 2013 11:57:01 -0700 + +ceph (0.66-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 08 Jul 2013 15:44:45 -0700 + +ceph (0.65-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 25 Jun 2013 09:19:14 -0700 + +ceph (0.64-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Wed, 12 Jun 2013 09:53:54 -0700 + +ceph (0.63-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 28 May 2013 13:57:53 -0700 + +ceph (0.62) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 14 May 2013 09:08:21 -0700 + +ceph (0.61-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 06 May 2013 13:18:43 -0700 + +ceph (0.60-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 01 Apr 2013 12:22:30 -0700 + +ceph (0.59-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 19 Mar 2013 22:26:37 -0700 + +ceph (0.58-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 04 Mar 2013 15:17:58 -0800 + +ceph (0.57-1) quantal; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 19 Feb 2013 10:06:39 -0800 + +ceph (0.56-1) quantal; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 31 Dec 2012 17:08:45 -0800 + +ceph (0.55.1-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Wed, 12 Dec 2012 16:24:13 -0800 + +ceph (0.55-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Mon, 03 Dec 2012 19:08:14 -0800 + +ceph (0.54-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 13 Nov 2012 13:17:19 -0800 + +ceph (0.53-1) precise; urgency=low + + * New upstream release + + -- Gary Lowell Tue, 16 Oct 2012 17:40:46 +0000 + +ceph (0.52-1) precise; urgency=low + + * New upstream release + + -- Ubuntu Thu, 27 Sep 2012 16:16:52 +0000 + +ceph (0.51-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sat, 25 Aug 2012 15:58:23 -0700 + +ceph (0.50-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 13 Aug 2012 09:44:40 -0700 + +ceph (0.49-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 20 Jul 2012 23:26:43 -0700 + +ceph (0.48argonaut-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sat, 30 Jun 2012 14:49:30 -0700 + +ceph (0.47.3-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Wed, 20 Jun 2012 10:57:03 -0700 + +ceph (0.47.2-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Wed, 23 May 2012 09:00:43 -0700 + +ceph (0.47.1-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 21 May 2012 14:28:30 -0700 + +ceph (0.47-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sun, 20 May 2012 15:16:03 -0700 + +ceph (0.46-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sun, 29 Apr 2012 21:21:01 -0700 + +ceph (0.45-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Tue, 10 Apr 2012 10:41:57 -0700 + +ceph (0.44.2-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Thu, 05 Apr 2012 14:54:17 -0700 + +ceph (0.44.1-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Tue, 27 Mar 2012 13:02:00 -0700 + +ceph (0.44-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sun, 18 Mar 2012 12:03:38 -0700 + +ceph (0.43-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 02 Mar 2012 08:53:10 -0800 + +ceph (0.42.2-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 24 Feb 2012 12:59:38 -0800 + +ceph (0.42.1-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Thu, 23 Feb 2012 18:46:23 -0800 + +ceph (0.42-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sun, 19 Feb 2012 15:30:20 -0800 + +ceph (0.41-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 27 Jan 2012 10:42:11 -0800 + +ceph (0.40-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 13 Jan 2012 08:36:02 -0800 + +ceph (0.39-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 02 Dec 2011 09:01:20 -0800 + +ceph (0.38-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Thu, 10 Nov 2011 15:06:44 -0800 + +ceph (0.37-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 17 Oct 2011 08:35:42 -0700 + +ceph (0.36-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 30 Sep 2011 09:29:29 -0700 + +ceph (0.35-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Wed, 21 Sep 2011 09:36:03 -0700 + +ceph (0.34-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 26 Aug 2011 21:48:35 -0700 + +ceph (0.33-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Mon, 15 Aug 2011 16:42:07 -0700 + +ceph (0.32-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 29 Jul 2011 21:42:08 -0700 + +ceph (0.30-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 27 Jun 2011 20:06:06 -0700 + +ceph (0.29.1-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Thu, 16 Jun 2011 13:10:47 -0700 + +ceph (0.29-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 06 Jun 2011 09:59:25 -0700 + +ceph (0.28.2-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Sat, 28 May 2011 09:14:17 -0700 + +ceph (0.28.1-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Mon, 23 May 2011 21:11:30 -0700 + +ceph (0.28-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Tue, 17 May 2011 18:03:11 -0700 + +ceph (0.27.1-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Thu, 05 May 2011 13:42:06 -0700 + +ceph (0.27-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Fri, 22 Apr 2011 16:51:49 -0700 + +ceph (0.26-1) experimental; urgency=low + + * New upstream release. + * Make Ceph Linux only and build on all Linux archs (closes: #614890), + but only build-depend google-perftools on x86 and x64 archs only. + * Correct section of libcrush1, librados1, librbd1 and libceph1 to libs. + * Make Ceph cross buildable (closes: #618939), thanks to Hector Oron. + * Disable libatomic-ops on ARMv4t (armel) archs to prevent FTBFS + (closes: #615235), thanks go to Hector Oron again. + * Rename librados1{,-dbg,-dev} packages to librados2{,-dbg,-dev} ones; + conflict with and replace the former ones. + + -- Laszlo Boszormenyi (GCS) Fri, 01 Apr 2011 16:28:11 +0100 + +ceph (0.25.2-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Sun, 20 Mar 2011 21:07:38 -0700 + +ceph (0.25.1-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Mon, 14 Mar 2011 14:43:47 -0700 + +ceph (0.25-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Fri, 04 Mar 2011 14:39:54 -0800 + +ceph (0.24.3-1) experimental; urgency=low + + * New upstream release + + -- Sage Weil Thu, 10 Feb 2011 09:14:00 -0800 + +ceph (0.24.2-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Mon, 24 Jan 2011 11:02:24 -0800 + +ceph (0.24.1-1) experimental; urgency=low + + * New upstream release. + + -- Sage Weil Fri, 07 Jan 2011 16:49:48 -0800 + +ceph (0.24-1) experimental; urgency=low + + * New upstream release. + + -- Laszlo Boszormenyi (GCS) Wed, 01 Dec 2010 09:26:25 -0800 + +ceph (0.23.1-1) experimental; urgency=low + + * Initial release (Closes: #506040) + + -- Sage Weil Sun, 21 Nov 2010 15:22:21 -0800 diff --git a/ceph/ceph/debian/deb_folder/clean b/ceph/ceph/debian/deb_folder/clean new file mode 100644 index 000000000..8638ac91a --- /dev/null +++ b/ceph/ceph/debian/deb_folder/clean @@ -0,0 +1,32 @@ +configure +src/rocksdb/util/build_version.cc +src/pybind/*.pyc +src/test/pybind/*.pyc +src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock.sln +src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock.vcproj +src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_config.vsprops +src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_main.vcproj +src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_test.vcproj +src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock.sln +src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock.vcxproj +src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_config.props +src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_main.vcxproj +src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_test.vcxproj +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.cbproj +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.groupproj +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_all.cc +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_link.cc +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_main.cbproj +src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_unittest.cbproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest-md.sln +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest-md.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.sln +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_main-md.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_main.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_prod_test-md.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_prod_test.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_unittest-md.vcproj +src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_unittest.vcproj +debian/ceph-common.logrotate +debian/radosgw.init diff --git a/ceph/ceph/debian/deb_folder/compat b/ceph/ceph/debian/deb_folder/compat new file mode 100644 index 000000000..f599e28b8 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/compat @@ -0,0 +1 @@ +10 diff --git a/ceph/ceph/debian/deb_folder/control b/ceph/ceph/debian/deb_folder/control new file mode 100644 index 000000000..b86779325 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/control @@ -0,0 +1,849 @@ +Source: ceph +Section: admin +Priority: optional +Maintainer: Ceph Packaging Team +Uploaders: + James Page , + Gaudenz Steinlin , + Bernd Zeimetz , + Thomas Goirand , +Build-Depends: + cmake, + cython3, + debhelper (>= 10~), + default-jdk, + dh-exec, + dh-python, + dpkg-dev (>= 1.16.1~), + gperf, + javahelper, + junit4, + libaio-dev, + libbabeltrace-ctf-dev, + libbabeltrace-dev, + libblkid-dev (>= 2.17), + libboost-atomic-dev (>= 1.67.0), + libboost-chrono-dev (>= 1.67.0), + libboost-context-dev (>= 1.67.0) [!s390x !mips64el !ia64 !m68k !ppc64 !riscv64 !sh4 !sparc64 !x32 !alpha], + libboost-coroutine-dev (>= 1.67.0) [!s390x !mips64el !ia64 !m68k !ppc64 !riscv64 !sh4 !sparc64 !x32 !alpha], + libboost-date-time-dev (>= 1.67.0), + libboost-iostreams-dev (>= 1.67.0), + libboost-program-options-dev (>= 1.67.0), + libboost-python-dev (>= 1.67.0), + libboost-random-dev (>= 1.67.0), + libboost-regex-dev (>= 1.67.0), + libboost-system-dev (>= 1.67.0), + libboost-thread-dev (>= 1.67.0), + libbz2-dev, + libcap-ng-dev, + libcunit1-dev, + libcurl4-gnutls-dev, + libedit-dev, + libexpat1-dev, + libfuse-dev, + libgoogle-perftools-dev [i386 amd64 powerpc armhf arm64 ppc64el], + libibverbs-dev, + libkeyutils-dev, + libldap2-dev, + libleveldb-dev, + liblz4-dev (>= 0.0~r131), + libncurses-dev, + libnl-3-dev, + libnl-genl-3-dev, + libnss3-dev, + liboath-dev, + librabbitmq-dev, + librdkafka-dev, + librdmacm-dev, + libsnappy-dev, + libssl-dev, + libtool, + libudev-dev, + libxml2-dev, + lsb-release, + pkg-config, + python3-cherrypy3, + python3-dev, + python3-pecan, + python3-setuptools, + python3-sphinx, + tox, + uuid-runtime, + valgrind [amd64 armhf i386 powerpc], + virtualenv, + xfslibs-dev, + yasm [amd64], + zlib1g-dev, +Build-Conflicts: + libcrypto++-dev, +Standards-Version: 4.2.1 +Vcs-Git: https://salsa.debian.org/ceph-team/ceph.git +Vcs-Browser: https://salsa.debian.org/ceph-team/ceph +Homepage: http://ceph.com/ + +Package: ceph +Architecture: linux-any +Depends: + ceph-mgr (= ${binary:Version}), + ceph-mon (= ${binary:Version}), + ceph-osd (= ${binary:Version}), + ${misc:Depends}, +Suggests: + ceph-mds (= ${binary:Version}), +Description: distributed storage and file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + +Package: ceph-base +Architecture: linux-any +Depends: + binutils, + ceph-common (= ${binary:Version}), + cryptsetup-bin | cryptsetup, + gdisk, + hdparm | sdparm, + parted, + uuid-runtime, + xfsprogs, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Pre-Depends: + ${misc:Pre-Depends}, +Breaks: + ceph (<< 10.2.2-0ubuntu2~), + ceph-common (<< 9.2.0-0~), + ceph-test (<< 12.2.8+dfsg1-1~), + python-ceph (<< 0.94.1-1~), +Replaces: + ceph (<< 12.2.8+dfsg1-1~), + ceph-common (<< 9.2.0-0~), + ceph-test (<< 12.2.8+dfsg1-1~), + python-ceph (<< 0.94.1-1~), +Recommends: + ceph-mds (= ${binary:Version}), + chrony | time-daemon | ntp, + librados2 (= ${binary:Version}), + librbd1 (= ${binary:Version}), +Suggests: + btrfs-tools, + logrotate, +Description: common ceph daemon libraries and management tools + Ceph is a distributed storage system designed to provide excellent + performance, reliability, and scalability. + . + This package contains the libraries and management tools that are common among + the Ceph server daemons (ceph-mon, ceph-mgr, ceph-osd, ceph-mds). These tools + are necessary for creating, running, and administering a Ceph storage cluster. + +Package: ceph-common +Architecture: linux-any +Depends: + librbd1 (= ${binary:Version}), + python3-cephfs (= ${binary:Version}), + python3-prettytable, + python3-rados (= ${binary:Version}), + python3-rbd (= ${binary:Version}), + python3-requests, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Conflicts: + ceph-client-tools, +Breaks: + ceph (<< 9.2.0-0~), + ceph-base (<< 12.2.10+dfsg1-1~), + ceph-fs-common (<< 12.2.10+dfsg1-1~), + ceph-mds (<< 14.2.5-3~), + ceph-test (<< 9.2.0-0~), + librbd1 (<< 0.94.1-1~), + python-ceph (<< 0.94.1-1~), + radosgw (<< 12.0.3-0~), +Replaces: + ceph (<< 9.2.0-0~), + ceph-client-tools, + ceph-fs-common (<< 12.2.8+dfsg1-1~), + ceph-mds (<< 14.2.5-3~), + ceph-test (<< 9.2.0-1~), + librbd1 (<< 0.94.1-1~), + python-ceph (<< 0.94.1-1~), + radosgw (<< 12.0.3-0~), +Suggests: + ceph, + ceph-mds, +Description: common utilities to mount and interact with a ceph storage cluster + Ceph is a distributed storage and file system designed to provide + excellent performance, reliability, and scalability. This is a collection + of common tools that allow one to interact with and administer a Ceph cluster. + +Package: ceph-fuse +Architecture: amd64 +Depends: + python3, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Recommends: + fuse, +Description: FUSE-based client for the Ceph distributed file system + Ceph is a distributed network file system designed to provide + excellent performance, reliability, and scalability. This is a + FUSE-based client that allows one to mount a Ceph file system without + root privileges. + . + Because the FUSE-based client has certain inherent performance + limitations, it is recommended that the native Linux kernel client + be used if possible. If it is not practical to load a kernel module + (insufficient privileges, older kernel, etc.), then the FUSE client will + do. + +Package: ceph-mds +Architecture: linux-any +Depends: + ceph, + ${misc:Depends}, + ${shlibs:Depends}, +Recommends: + ceph-common, + ceph-fuse, + libcephfs2, +Breaks: + ceph (<< 0.67.3-1), +Replaces: + ceph (<< 0.67.3-1), +Description: metadata server for the ceph distributed file system + Ceph is a distributed storage and network file system designed to + provide excellent performance, reliability, and scalability. + . + This package contains the metadata server daemon, which is used to + create a distributed file system on top of the ceph storage cluster. + +Package: ceph-mgr +Architecture: linux-any +Depends: + ceph-base (= ${binary:Version}), + python3-bcrypt, + python3-cherrypy3, + python3-jwt, + python3-openssl, + python3-pecan, + python3-werkzeug, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Replaces: + ceph (<< 0.93-417), +Breaks: + ceph (<< 0.93-417), +Suggests: + ceph-mgr-dashboard, + ceph-mgr-diskprediction-cloud, + ceph-mgr-diskprediction-local, + ceph-mgr-rook, + ceph-mgr-ssh, +Description: manager for the ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the manager daemon, which is used to expose high + level management and monitoring functionality. + +Package: ceph-mgr-dashboard +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + python3-bcrypt, + python3-cherrypy3, + python3-distutils, + python3-jwt, + python3-openssl, + python3-routes, + python3-werkzeug, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: dashboard plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package provides a ceph-mgr plugin, providing a web-based + application to monitor and manage many aspects of a Ceph cluster and + related components. + . + See the Dashboard documentation at http://docs.ceph.com/ for details + and a detailed feature overview. + +Package: ceph-mgr-diskprediction-cloud +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: diskprediction-cloud plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the diskprediction_cloud plugin for the ceph-mgr + daemon, which helps predict disk failures. + +Package: ceph-mgr-diskprediction-local +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + python3-numpy, + python3-scipy, + python3-sklearn, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: diskprediction-local plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the diskprediction_local plugin for the ceph-mgr + daemon, which helps predict disk failures. + +Package: ceph-mgr-k8sevents +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + python3-kubernetes, + ${misc:Depends}, + ${python:Depends}, +Description: kubernetes events plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the k8sevents plugin, to allow ceph-mgr to send + ceph related events to the kubernetes events API, and track all events + that occur within the rook-ceph namespace. + +Package: ceph-mgr-rook +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + python3-six, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: rook plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the rook plugin for ceph-mgr's orchestration + functionality, to allow ceph-mgr to install and configure ceph using + Rook. + +Package: ceph-mgr-ssh +Architecture: all +Depends: + ceph-mgr (>= ${binary:Version}), + python3-six, + ${misc:Depends}, + ${python3:Depends}, +Description: ssh orchestrator plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the SSH plugin for ceph-mgr's orchestration + functionality, to allow ceph-mgr to perform orchestration functions + over a standard SSH connection. + +Package: ceph-mon +Architecture: linux-any +Depends: + ceph-base (= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Replaces: + ceph (<< 10.2.2-0ubuntu2~), +Breaks: + ceph (<< 10.2.2-0ubuntu2~), +Description: monitor server for the ceph storage system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the cluster monitor daemon for the Ceph storage + system. One or more instances of ceph-mon form a Paxos part-time parliament + cluster that provides extremely reliable and durable storage of cluster + membership, configuration, and state. + +Package: ceph-osd +Architecture: linux-any +Depends: + ceph-base (= ${binary:Version}), + lvm2, + smartmontools (>= 7.0), + sudo, + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Suggests: + nvme-cli, +Pre-Depends: + ceph-common (= ${binary:Version}), +Replaces: + ceph (<< 10.2.2-0ubuntu2~), + ceph-test (<< 12.2.8+dfsg1-1~), +Breaks: + ceph (<< 10.2.2-0ubuntu2~), + ceph-test (<< 12.2.8+dfsg1-1~), +Description: OSD server for the ceph storage system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the Object Storage Daemon for the Ceph storage system. + It is responsible for storing objects on a local file system + and providing access to them over the network. + +Package: ceph-resource-agents +Architecture: all +Priority: optional +Recommends: + pacemaker, +Depends: + ceph (>= ${binary:Version}), + resource-agents, + ${misc:Depends}, +Description: OCF-compliant resource agents for Ceph + Ceph is a distributed storage and network file system designed to provide + excellent performance, reliability, and scalability. + . + This package contains the resource agents (RAs) which integrate + Ceph with OCF-compliant cluster resource managers, + such as Pacemaker. + +Package: cephfs-shell +Architecture: all +Depends: + ${misc:Depends}, + ${python3:Depends}, +Description: interactive shell for the Ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. This is an interactive tool that + allows accessing a Ceph file system without mounting it by providing + a nice pseudo-shell which works like an FTP client. + . + This package contains a CLI for interacting with the CephFS. + +Package: libcephfs-dev +Architecture: linux-any +Section: libdevel +Depends: + libcephfs2 (= ${binary:Version}), + ${misc:Depends}, +Conflicts: + libceph-dev, + libceph1-dev, + libcephfs2-dev, +Replaces: + libceph-dev, + libceph1-dev, + libcephfs2-dev, +Description: Ceph distributed file system client library (development files) + Ceph is a distributed network file system designed to provide + excellent performance, reliability, and scalability. This is a + shared library allowing applications to access a Ceph distributed + file system via a POSIX-like interface. + . + This package contains development files needed for building applications that + link against libcephfs2. + +Package: libcephfs-java +Architecture: all +Section: java +Depends: + libcephfs-jni (>= ${binary:Version}), + ${java:Depends}, + ${misc:Depends}, +Description: Java library for the Ceph File System + Ceph is a distributed storage system designed to provide excellent + performance, reliability, and scalability. + . + This package contains the Java library for interacting with the Ceph + File System. + +Package: libcephfs-jni +Architecture: linux-any +Section: libs +Depends: + libcephfs2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Description: Java Native Interface library for CephFS Java bindings + Ceph is a distributed storage system designed to provide excellent + performance, reliability, and scalability. + . + This package contains the Java Native Interface library for interacting + with the Ceph File System. + +Package: libcephfs2 +Architecture: linux-any +Section: libs +Conflicts: + libceph, + libceph1, + libcephfs, +Replaces: + libceph, + libceph1, + libcephfs, +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Pre-Depends: + ${misc:Pre-Depends}, +Description: Ceph distributed file system client library + Ceph is a distributed network file system designed to provide + excellent performance, reliability, and scalability. This is a + shared library allowing applications to access a Ceph distributed + file system via a POSIX-like interface. + +Package: librados-dev +Architecture: linux-any +Section: libdevel +Depends: + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Conflicts: + librados1-dev, + librados2-dev, +Replaces: + librados1-dev, + librados2-dev, +Description: RADOS distributed object store client library (development files) + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. + . + This package contains development files needed + for building applications that link against librados2. + +Package: librados2 +Architecture: linux-any +Section: libs +Conflicts: + librados, + librados1, +Replaces: + librados, + librados1, +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Pre-Depends: + ${misc:Pre-Depends}, +Description: RADOS distributed object store client library + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. + +Package: libradospp-dev +Architecture: linux-any +Section: libdevel +Depends: + librados-dev (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Description: RADOS distributed object store client C++ library (development files) + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. + . + This package contains development files needed for building C++ applications that + link against librados. + +Package: libradosstriper-dev +Architecture: linux-any +Section: libdevel +Depends: + libradosstriper1 (= ${binary:Version}), + ${misc:Depends}, +Description: RADOS striping interface (development files) + libradosstriper is a striping interface built on top of the rados + library, allowing to stripe bigger objects onto several standard + rados objects using an interface very similar to the rados one. + . + This package contains development files needed for building applications that + link against libradosstriper. + +Package: libradosstriper1 +Architecture: linux-any +Section: libs +Depends: + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Description: RADOS striping interface + Striping interface built on top of the rados library, allowing + to stripe bigger objects onto several standard rados objects using + an interface very similar to the rados one. + +Package: librbd-dev +Architecture: linux-any +Section: libdevel +Depends: + librados-dev, + librbd1 (= ${binary:Version}), + ${misc:Depends}, +Conflicts: + librbd1-dev, +Replaces: + librbd1-dev, +Description: RADOS block device client library (development files) + RBD is a block device striped across multiple distributed objects + in RADOS, a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to manage these block devices. + . + This package contains development files needed for building applications that + link against librbd1. + +Package: librbd1 +Architecture: linux-any +Section: libs +Depends: + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Pre-Depends: + ${misc:Pre-Depends}, +Description: RADOS block device client library + RBD is a block device striped across multiple distributed objects + in RADOS, a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to manage these block devices. + +Package: librgw-dev +Architecture: linux-any +Section: libdevel +Depends: + librados-dev (= ${binary:Version}), + librgw2 (= ${binary:Version}), + ${misc:Depends}, +Description: RADOS client library (development files) + RADOS is a distributed object store used by the Ceph distributed + storage system. This package provides a REST gateway to the + object store that aims to implement a superset of Amazon's S3 + service. + . + This package contains development files needed for building applications + that link against librgw2. + +Package: librgw2 +Architecture: linux-any +Section: libs +Depends: + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Description: RADOS Gateway client library + RADOS is a distributed object store used by the Ceph distributed + storage system. This package provides a REST gateway to the + object store that aims to implement a superset of Amazon's S3 + service. + . + This package contains the library interface and headers only. + +Package: python3-ceph +Architecture: all +Section: python +Depends: + python3-cephfs (<< ${source:Version}.1~), + python3-cephfs (>= ${source:Version}), + python3-rados (<< ${source:Version}.1~), + python3-rados (>= ${source:Version}), + python3-rbd (<< ${source:Version}.1~), + python3-rbd (>= ${source:Version}), + python3-rgw (<< ${source:Version}.1~), + python3-rgw (>= ${source:Version}), + ${misc:Depends}, +Description: Meta-package for all Python 3.x modules for the Ceph libraries + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package is a metapackage for all Ceph Python 3.x bindings. + +Package: python3-ceph-argparse +Architecture: linux-any +Section: python +Depends: + ${misc:Depends}, + ${python3:Depends}, +Breaks: + ceph-common (<< 14.2.1-0~), +Replaces: + ceph-common (<< 14.2.1-0~), +Description: Python 3 utility libraries for Ceph CLI + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains types and routines for Python 3 used by the + Ceph CLI as well as the RESTful interface. + +Package: python3-cephfs +Architecture: linux-any +Section: python +Depends: + libcephfs2 (= ${binary:Version}), + python3-ceph-argparse (= ${binary:Version}), + python3-rados (= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph libcephfs library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + CephFS file system client library. + +Package: python3-rados +Architecture: linux-any +Section: python +Depends: + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librados library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RADOS object storage. + +Package: python3-rbd +Architecture: linux-any +Section: python +Depends: + librbd1 (>= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librbd library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RBD block device library. + +Package: python3-rgw +Architecture: linux-any +Section: python +Depends: + librgw2 (>= ${binary:Version}), + python3-rados (= ${binary:Version}), + ${misc:Depends}, + ${python3:Depends}, + ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librgw library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RGW library. + +Package: rados-objclass-dev +Architecture: linux-any +Section: libdevel +Depends: + librados-dev (= ${binary:Version}), + ${misc:Depends}, +Description: RADOS object class development kit + This package contains development files needed for building + RADOS object class plugins. + +Package: radosgw +Architecture: linux-any +Depends: + ceph-common (= ${binary:Version}), + librgw2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Suggests: + logrotate, +Description: REST gateway for RADOS distributed object store + RADOS is a distributed object store used by the Ceph distributed + storage system. This package provides a REST gateway to the + object store that aims to implement a superset of Amazon's S3 + service as well as the OpenStack Object Storage ("Swift") API. + . + This package contains the proxy daemon and related tools only. + +Package: rbd-fuse +Architecture: linux-any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Recommends: + fuse, +Description: FUSE-based rbd client for the Ceph distributed file system + Ceph is a distributed network file system designed to provide + excellent performance, reliability, and scalability. This is a + FUSE-based client that allows one to map Ceph rbd images as files. + +Package: rbd-mirror +Architecture: linux-any +Depends: + ceph-common (= ${binary:Version}), + librados2 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends}, +Description: Ceph daemon for mirroring RBD images + Ceph is a distributed storage system designed to provide excellent + performance, reliability, and scalability. + . + This package provides a daemon for mirroring RBD images between + Ceph clusters, streaming changes asynchronously. + +Package: rbd-nbd +Architecture: linux-any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: NBD-based rbd client for the Ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. This is a + NBD-based client that allows one to map Ceph rbd images as local + block device. + . + NBD base client that allows one to map Ceph rbd images as local + block device. + +# Added in from centos/ceph.spec +Package: ceph-grafana-dashboards +Architecture: linux-any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: Set of Grafana dashboards for monitoring purposes + This package provides a set of Grafana dashboards for monitoring of + Ceph clusters. The dashboards require a Prometheus server setup + collecting data from Ceph Manager "prometheus" module and Prometheus + project "node_exporter" module. The dashboards are designed to be + integrated with the Ceph Manager Dashboard web UI. diff --git a/ceph/ceph/debian/deb_folder/copyright b/ceph/ceph/debian/deb_folder/copyright new file mode 100644 index 000000000..390a68a96 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/copyright @@ -0,0 +1,977 @@ +Format-Specification: http://anonscm.debian.org/viewvc/dep/web/deps/dep5/copyright-format.xml?revision=279&view=markup +Name: ceph +Maintainer: Sage Weil +Source: http://ceph.com/ + +Files: * +Copyright: 2004-2014 Sage Weil + 2004-2014 Inktank + Inktank, Inc + Inktank Storage, Inc. + 2012-2014 Red Hat + 2013-2014 Cloudwatt + 2013 CohortFS, LLC + 2004-2011 Dreamhost + 2013 eNovance SAS + 2014 Adam Crume + 2012 Florian Haas, hastexo + 2010 Greg Farnum + 2014 John Spray + 2011 Stanislav Sedov + 2013-2014 UnitedStack + 2011 Wido den Hollander +License: LGPL2.1 (see COPYING-LGPL2.1) + +Files: cmake/modules/FindLTTngUST.cmake +Copyright: + Copyright 2016 Kitware, Inc. + Copyright 2016 Philippe Proulx +License: BSD 3-clause + +Files: doc/* +Copyright: (c) 2010-2012 New Dream Network and contributors +License: Creative Commons Attribution Share Alike 3.0 (CC-BY-SA-3.0) + +Files: bin/git-archive-all.sh +License: GPL3 + +Files: src/mount/canonicalize.c +Copyright: Copyright (C) 1993 Rick Sladkey +License: LGPL2 or later (see COPYING-GPL2) + + +Files: src/os/btrfs_ioctl.h +Copyright: Copyright (C) 2007 Oracle. All rights reserved. +License: GPL2 (see COPYING-GPL2) + +Files: src/include/ceph_hash.cc +Copyright: None +License: Public domain + +Files: src/common/bloom_filter.hpp +Copyright: Copyright (C) 2000 Arash Partow +License: Boost Software License, Version 1.0 + +Files: src/common/crc32c_intel*: +Copyright: + Copyright 2012-2013 Intel Corporation All Rights Reserved. +License: BSD 3-clause + +Files: src/common/sctp_crc32.c: +Copyright: + Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved +License: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + a) Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + b) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + c) Neither the name of Cisco Systems, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +Files: src/json_spirit +Copyright: + Copyright John W. Wilkinson 2007 - 2011 +License: + The MIT License + + Copyright (c) 2007 - 2010 John W. Wilkinson + + 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. + +Files: src/test/common/Throttle.cc src/test/filestore/chain_xattr.cc +Copyright: Copyright (C) 2013 Cloudwatt +License: LGPL2.1 or later + + + +Files: src/osd/ErasureCodePluginJerasure/*.{c,h} +Copyright: Copyright (c) 2011, James S. Plank +License: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the University of Tennessee nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + +Files: qa/workunits/erasure-code/jquery.js +Copyright: 2012 jQuery Foundation and other contributors +License: MIT + +Files: qa/workunits/erasure-code/jquery.{flot.categories,flot}.js +Copyright: 2007-2014 IOLA and Ole Laursen. +License: MIT + +Files: src/include/timegm.h +Copyright: Howard Hinnant + 2010-2011 Vicente J. Botet Escriba +License: Boost Software License, Version 1.0 + +Files: src/pybind/mgr/diskprediction_local/models/* +Copyright: None +License: Public domain + +Files: src/ceph-volume/plugin/zfs/* +Copyright: 2018, Willem Jan Withagen +License: BSD 3-clause + + +Files: src/test/perf_local.cc +Copyright: + (c) 2011-2014 Stanford University + (c) 2011 Facebook +License: + The MIT License + + +Comment: ----------------------------------------------- + Content above is taken from upstream's COPYING file. + Unfortunately it is incomplete. Debian/Ubuntu packaging + findings/additions are below. + ------------------------------------------------------- + + +Files: src/erasure-code/jerasure/ErasureCode* + src/erasure-code/ErasureCode* + src/erasure-code/isa/* + src/include/str_map.h + src/test/common/test_str_map.cc + src/test/erasure-code/* + src/test/rgw/test_rgw_manifest.cc +Copyright: 2014 CERN/Switzerland + 2013-2014 Cloudwatt + 2014 Red Hat + 2013 eNovance SAS +License: LGPL-2.1+ + + +Files: src/erasure-code/isa/isa-l/erasure_code/* +Copyright: 2011-2014 Intel Corporation +License: BSD-3-clause + +Files: src/rocksdb/* +Copyright: 2004-2013 Facebook, Inc. + 2011 The LevelDB Authors + 2009 Google Inc. +License: BSD-3-clause +Comment: + Additional Grant of Patent Rights + . + “Software” means the rocksdb software distributed by Facebook, Inc. + . + Facebook hereby grants you a perpetual, worldwide, royalty-free, + non-exclusive, irrevocable (subject to the termination provision below) + license under any rights in any patent claims owned by Facebook, to make, + have made, use, sell, offer to sell, import, and otherwise transfer the + Software. For avoidance of doubt, no license is granted under Facebook’s + rights in any patent claims that are infringed by (i) modifications to the + Software made by you or a third party, or (ii) the Software in combination + with any software or other technology provided by you or a third party. + . + The license granted hereunder will terminate, automatically and without + notice, for anyone that makes any claim (including by filing any lawsuit, + assertion or other action) alleging (a) direct, indirect, or contributory + infringement or inducement to infringe any patent: (i) by Facebook or any + of its subsidiaries or affiliates, whether or not such claim is related + to the Software, (ii) by any party if such claim arises in whole or in + part from any software, product or service of Facebook or any of its + subsidiaries or affiliates, whether or not such claim is related to the + Software, or (iii) by any party relating to the Software; or (b) that + any right in any patent claim of Facebook is invalid or unenforceable. + + +Files: src/rocksdb/util/xxhash.* +Copyright: 2012-2014, Yann Collet. +License: BSD-2-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Files: src/mount/canonicalize.c + src/test/common/test_config.cc + src/test/crush/TestCrushWrapper.cc + src/test/common/Throttle.cc + src/test/objectstore/chain_xattr.cc + src/test/mon/mon-test-helpers.sh + src/test/objectstore/chain_xattr.cc + src/test/osd/osd-test-helpers.sh + src/ceph-disk + src/stop.sh +Copyright: 1993 Rick Sladkey + 2013 Inktank + 2013-2014 Cloudwatt +License: LGPL-2+ + +Files: src/os/btrfs_ioctl.h + src/test/mon/PGMap.cc +Copyright: 2007 Oracle. All rights reserved. + 2014 Inktank +License: GPL-2 + +Files: src/common/ceph_hash.cc +Copyright: 1995-1997 Robert J. Jenkins Jr. +License: public-domain + This file uses Robert Jenkin's hash function as detailed at: + . + http://burtleburtle.net/bob/hash/evahash.html + . + This is in the public domain. + +Files: src/common/bloom_filter.hpp +Copyright: 2000 Arash Partow +License: Boost-Software-License-1.0 + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole or + in part, and all derivative works of the Software, unless such copies + or derivative works are solely in the form of machine-executable object + code generated by a source language processor. + . + 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: src/common/crc32c_intel* +Copyright: 2012-2013 Intel Corporation All Rights Reserved. +License: BSD-3-clause + +Files: src/common/sctp_crc32.c +Copyright: 2001-2007, by Cisco Systems, Inc. All rights reserved, + 2004-2006 Intel Corporation - All Rights Reserved +License: BSD-3-clause + +Files: src/erasure-code/jerasure/gf-complete/*/* +Copyright: 2013 James S. Plank + Ethan L. Miller + Kevin M. Greenan + Benjamin A. Arnold + John A. Burnum + Adam W. Disney + Allen C. McBride +License: BSD-3-clause +Comment: + https://bitbucket.org/jimplank/gf-complete + +Files: src/erasure-code/jerasure/jerasure/*/* +Copyright: 2011-2013 James S. Plank + 2013 Kevin Greenan +License: BSD-3-clause + +Files: src/gtest/* +Copyright: 2008, Google Inc. +License: BSD-3-clause + +Files: src/civetweb/* +Copyright: 2004-2013 Sergey Lyubka + 2013-2014 the Civetweb developers +License: Expat + +Files: src/json_spirit/* +Copyright: 2007-2011, John W. Wilkinson +License: Expat + +Files: src/java/native/ScopedLocalRef.h + src/java/native/JniConstants.* +Copyright: 2010 The Android Open Source Project +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + The complete text of the Apache License, Version 2.0 + can be found in "/usr/share/common-licenses/Apache-2.0". + +Files: src/libs3/* +Copyright: 2008 Bryan Ischo +License: GPL-3/OpenSSL + libs3 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, version 3 of the License. + . + In addition, as a special exception, the copyright holders give + permission to link the code of this library and its programs with the + OpenSSL library, and distribute linked combinations including the two. + . + libs3 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. + . + The complete text of the GNU General Public License version 3 + can be found in "/usr/share/common-licenses/GPL-3' file. + + +Files: src/mount/mtab.c +Copyright: util-linux-ng AUTHORS +License: GPL-2+ +Comment: + "mount/fstab.c" from line 559: + https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/mount-deprecated/fstab.c?h=v2.22#n559 + https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/README.licensing + +Files: src/test/librbd/fsx.c +Copyright: 1991, NeXT Computer, Inc. +License: APSL-2.0 + The contents of this file constitute Original Code as defined in and + are subject to the Apple Public Source License Version 2.0 (the + "License"). You may not use this file except in compliance with the + License. Please obtain a copy of the License at + http://www.opensource.apple.com/apsl/ and read it before using this file. + . + This Original Code and all software distributed under the License are + distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + License for the specific language governing rights and limitations + under the License. +Comment: + http://codemonkey.org.uk/projects/fsx/ + http://codemonkey.org.uk/projects/fsx/fsx-macosforge/fsx.c + +Files: man/* + debian/man/* +Copyright: 2010-2014, Inktank Storage, Inc. and contributors. +License: CC-BY-SA-3.0 + + +Files: debian/missing-sources/bootstrap.js +Copyright: 2011-2015 Twitter, Inc +License: MIT + +Files: debian/missing-sources/two.js +Copyright: 2012 - 2017 jonobr1 / http://jonobr1.com +License: MIT + +Files: debian/* +Copyright: 2010 Sage Weil + 2010 Canonical, Ltd. + 2011-2013 László Böszörményi (GCS) + 2013-2014 James Page + 2014 Dmitry Smirnov + 2019 Bernd Zeimetz +License: LGPL-2.1 + +License: GPL-2 + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in `/usr/share/common-licenses/GPL-2' file. + +License: GPL-2+ + 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 2 of the License, or + (at your option) any later version. + . + This package 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 . + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in `/usr/share/common-licenses/GPL-2'. + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + . + On Debian systems, the complete text of the GNU Lesser General + Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. + +License: LGPL-2.1+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + . + On Debian systems, the complete text of the GNU Lesser General + Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. + +License: LGPL-2+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2 (or later) as published by the Free Software Foundation. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + . + On Debian systems, the complete text of the GNU Lesser General + Public License 2 can be found in `/usr/share/common-licenses/LGPL-2'. + +License: Expat + 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. +Comment: + This license also known as "MIT" however FSF consider "MIT" labelling + ambiguous and copyright-format specification recommend to label such license + as "Expat". + +License: CC-BY-SA-3.0 + Creative Commons Attribution-ShareAlike 3.0 Unported + ․ + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION + ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE + INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + ITS USE. + ․ + License + ․ + THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE + COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY + COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS + AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + ․ + BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE + TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY + BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS + CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND + CONDITIONS. + ․ + 1. Definitions + ․ + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, derivative + work, arrangement of music or other alterations of a literary or + artistic work, or phonogram or performance and includes cinematographic + adaptations or any other form in which the Work may be recast, + transformed, or adapted including in any form recognizably derived from + the original, except that a work that constitutes a Collection will not + be considered an Adaptation for the purpose of this License. For the + avoidance of doubt, where the Work is a musical work, performance or + phonogram, the synchronization of the Work in timed-relation with a + moving image ("synching") will be considered an Adaptation for the + purpose of this License. + ․ + b. "Collection" means a collection of literary or artistic works, such + as encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed in + Section 1(f) below, which, by reason of the selection and arrangement of + their contents, constitute intellectual creations, in which the Work is + included in its entirety in unmodified form along with one or more other + contributions, each constituting separate and independent works in + themselves, which together are assembled into a collective whole. A work + that constitutes a Collection will not be considered an Adaptation (as + defined below) for the purposes of this License. + ․ + c. "Creative Commons Compatible License" means a license that is listed + at http://creativecommons.org/compatiblelicenses that has been approved + by Creative Commons as being essentially equivalent to this License, + including, at a minimum, because that license: (i) contains terms that + have the same purpose, meaning and effect as the License Elements of + this License; and, (ii) explicitly permits the relicensing of + adaptations of works made available under that license under this + License or a Creative Commons jurisdiction license with the same License + Elements as this License. + ․ + d. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or other + transfer of ownership. + ․ + e. "License Elements" means the following high-level license attributes + as selected by Licensor and indicated in the title of this License: + Attribution, ShareAlike. + ․ + f. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + ․ + g. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work or + if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + ․ + h. "Work" means the literary and/or artistic work offered under the + terms of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, pamphlet + and other writing; a lecture, address, sermon or other work of the same + nature; a dramatic or dramatico-musical work; a choreographic work or + entertainment in dumb show; a musical composition with or without words; + a cinematographic work to which are assimilated works expressed by a + process analogous to cinematography; a work of drawing, painting, + architecture, sculpture, engraving or lithography; a photographic work + to which are assimilated works expressed by a process analogous to + photography; a work of applied art; an illustration, map, plan, sketch + or three-dimensional work relative to geography, topography, + architecture or science; a performance; a broadcast; a phonogram; a + compilation of data to the extent it is protected as a copyrightable + work; or a work performed by a variety or circus performer to the extent + it is not otherwise considered a literary or artistic work. + ․ + i. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + ․ + j. "Publicly Perform" means to perform public recitations of the Work + and to communicate to the public those public recitations, by any means + or process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a place + individually chosen by them; to perform the Work to the public by any + means or process and the communication to the public of the performances + of the Work, including by public digital performance; to broadcast and + rebroadcast the Work by any means including signs, sounds or images. + ․ + k. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + ․ + 2. Fair Dealing Rights. Nothing in this License is intended to reduce, + limit, or restrict any uses free from copyright or rights arising from + limitations or exceptions that are provided for in connection with the + copyright protection under copyright law or other applicable laws. + ․ + 3. License Grant. Subject to the terms and conditions of this License, + Licensor hereby grants You a worldwide, royalty-free, non-exclusive, + perpetual (for the duration of the applicable copyright) license to + exercise the rights in the Work as stated below: + ․ + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + ․ + b. to create and Reproduce Adaptations provided that any such + Adaptation, including any translation in any medium, takes reasonable + steps to clearly label, demarcate or otherwise identify that changes + were made to the original Work. For example, a translation could be + marked "The original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + ․ + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + ․ + d. to Distribute and Publicly Perform Adaptations. + ․ + e. For the avoidance of doubt: + ․ + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or compulsory + licensing scheme cannot be waived, the Licensor reserves the exclusive + right to collect such royalties for any exercise by You of the rights + granted under this License; + ․ + ii. Waivable Compulsory License Schemes. In those jurisdictions in which + the right to collect royalties through any statutory or compulsory + licensing scheme can be waived, the Licensor waives the exclusive right + to collect such royalties for any exercise by You of the rights granted + under this License; and, + ․ + iii. Voluntary License Schemes. The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is a + member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights + granted under this License. + ․ + The above rights may be exercised in all media and formats whether now + known or hereafter devised. The above rights include the right to make + such modifications as are technically necessary to exercise the rights + in other media and formats. Subject to Section 8(f), all rights not + expressly granted by Licensor are hereby reserved. + ․ + 4. Restrictions. The license granted in Section 3 above is expressly + made subject to and limited by the following restrictions: + ․ + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms on + the Work that restrict the terms of this License or the ability of the + recipient of the Work to exercise the rights granted to that recipient + under the terms of the License. You may not sublicense the Work. You + must keep intact all notices that refer to this License and to the + disclaimer of warranties with every copy of the Work You Distribute or + Publicly Perform. When You Distribute or Publicly Perform the Work, You + may not impose any effective technological measures on the Work that + restrict the ability of a recipient of the Work from You to exercise the + rights granted to that recipient under the terms of the License. This + Section 4(a) applies to the Work as incorporated in a Collection, but + this does not require the Collection apart from the Work itself to be + made subject to the terms of this License. If You create a Collection, + upon notice from any Licensor You must, to the extent practicable, + remove from the Collection any credit as required by Section 4(c), as + requested. If You create an Adaptation, upon notice from any Licensor + You must, to the extent practicable, remove from the Adaptation any + credit as required by Section 4(c), as requested. + ․ + b. You may Distribute or Publicly Perform an Adaptation only under the + terms of: (i) this License; (ii) a later version of this License with + the same License Elements as this License; (iii) a Creative Commons + jurisdiction license (either this or a later license version) that + contains the same License Elements as this License (e.g., + Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible + License. If you license the Adaptation under one of the licenses + mentioned in (iv), you must comply with the terms of that license. If + you license the Adaptation under the terms of any of the licenses + mentioned in (i), (ii) or (iii) (the "Applicable License"), you must + comply with the terms of the Applicable License generally and the + following provisions: (I) You must include a copy of, or the URI for, + the Applicable License with every copy of each Adaptation You Distribute + or Publicly Perform; (II) You may not offer or impose any terms on the + Adaptation that restrict the terms of the Applicable License or the + ability of the recipient of the Adaptation to exercise the rights + granted to that recipient under the terms of the Applicable License; + (III) You must keep intact all notices that refer to the Applicable + License and to the disclaimer of warranties with every copy of the Work + as included in the Adaptation You Distribute or Publicly Perform; (IV) + when You Distribute or Publicly Perform the Adaptation, You may not + impose any effective technological measures on the Adaptation that + restrict the ability of a recipient of the Adaptation from You to + exercise the rights granted to that recipient under the terms of the + Applicable License. This Section 4(b) applies to the Adaptation as + incorporated in a Collection, but this does not require the Collection + apart from the Adaptation itself to be made subject to the terms of the + Applicable License. + ․ + c. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party or + parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party or + parties; (ii) the title of the Work if supplied; (iii) to the extent + reasonably practicable, the URI, if any, that Licensor specifies to be + associated with the Work, unless such URI does not refer to the + copyright notice or licensing information for the Work; and (iv) , + consistent with Ssection 3(b), in the case of an Adaptation, a credit + identifying the use of the Work in the Adaptation (e.g., "French + translation of the Work by Original Author," or "Screenplay based on + original Work by Original Author"). The credit required by this Section + 4(c) may be implemented in any reasonable manner; provided, however, + that in the case of a Adaptation or Collection, at a minimum such credit + will appear, if a credit for all contributing authors of the Adaptation + or Collection appears, then as part of these credits and in a manner at + least as prominent as the credits for the other contributing authors. + For the avoidance of doubt, You may only use the credit required by this + Section for the purpose of attribution in the manner set out above and, + by exercising Your rights under this License, You may not implicitly or + explicitly assert or imply any connection with, sponsorship or + endorsement by the Original Author, Licensor and/or Attribution Parties, + as appropriate, of You or Your use of the Work, without the separate, + express prior written permission of the Original Author, Licensor and/or + Attribution Parties. + ․ + d. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any Adaptations + or Collections, You must not distort, mutilate, modify or take other + derogatory action in relation to the Work which would be prejudicial to + the Original Author's honor or reputation. Licensor agrees that in those + jurisdictions (e.g. Japan), in which any exercise of the right granted + in Section 3(b) of this License (the right to make Adaptations) would be + deemed to be a distortion, mutilation, modification or other derogatory + action prejudicial to the Original Author's honor and reputation, the + Licensor will waive or not assert, as appropriate, this Section, to the + fullest extent permitted by the applicable national law, to enable You + to reasonably exercise Your right under Section 3(b) of this License + (right to make Adaptations) but not otherwise. + ․ + 5. Representations, Warranties and Disclaimer + ․ + UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR + OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY + KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, + INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, + FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF + LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, + WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + ․ + 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE + LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR + ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES + ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS + BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + ․ + 7. Termination + ․ + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + ․ + b. Subject to the above terms and conditions, the license granted here + is perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + ․ + 8. Miscellaneous + ․ + a. Each time You Distribute or Publicly Perform the Work or a + Collection, the Licensor offers to the recipient a license to the Work + on the same terms and conditions as the license granted to You under + this License. + ․ + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same terms + and conditions as the license granted to You under this License. + ․ + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + ․ + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + ․ + e. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that may + appear in any communication from You. This License may not be modified + without the mutual written agreement of the Licensor and You. + ․ + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and + the Universal Copyright Convention (as revised on July 24, 1971). These + rights and subject matter take effect in the relevant jurisdiction in + which the License terms are sought to be enforced according to the + corresponding provisions of the implementation of those treaty + provisions in the applicable national law. If the standard suite of + rights granted under applicable copyright law includes additional rights + not granted under this License, such additional rights are deemed to be + included in the License; this License is not intended to restrict the + license of any rights under applicable law. + ․ + ․ + Creative Commons Notice + ․ + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + ․ + Except for the limited purpose of indicating to the public that the Work + is licensed under the CCPL, Creative Commons does not authorize the use + by either party of the trademark "Creative Commons" or any related + trademark or logo of Creative Commons without the prior written consent + of Creative Commons. Any permitted use will be in compliance with + Creative Commons' then-current trademark usage guidelines, as may be + published on its website or otherwise made available upon request from + time to time. For the avoidance of doubt, this trademark restriction + does not form part of the License. + ․ + Creative Commons may be contacted at http://creativecommons.org/. + +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + . + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + . + 3. Neither the name of the copyright holder nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ceph/ceph/debian/deb_folder/lib-systemd/system-sleep/ceph b/ceph/ceph/debian/deb_folder/lib-systemd/system-sleep/ceph new file mode 100755 index 000000000..cf62f8321 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/lib-systemd/system-sleep/ceph @@ -0,0 +1,12 @@ +#!/bin/sh + +#/lib/systemd/system-sleep/ceph + +case $1 in +pre) + /bin/systemctl stop ceph +;; +post) + /bin/systemctl start ceph +;; +esac diff --git a/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-create-keys.service b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-create-keys.service new file mode 100644 index 000000000..4e29bc1e9 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-create-keys.service @@ -0,0 +1,9 @@ +[Unit] +Description=Create Ceph client.admin key when possible +PartOf=ceph-mon.service + +[Service] +Environment=CLUSTER=ceph +Environment=CONFIG=/etc/ceph/ceph.conf +EnvironmentFile=-/etc/default/ceph +ExecStart=/usr/sbin/ceph-create-keys --cluster ${CLUSTER} --id %H diff --git a/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mds.service b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mds.service new file mode 100644 index 000000000..86ff057f1 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mds.service @@ -0,0 +1,16 @@ +[Unit] +Description=Ceph metadata server daemon (MDS) +Documentation=man:ceph-mds +After=network-online.target nss-lookup.target +Wants=network-online.target nss-lookup.target +PartOf=ceph.target + +[Service] +LimitNOFILE=1048576 +LimitNPROC=1048576 +EnvironmentFile=-/etc/default/ceph +Environment=CLUSTER=ceph +ExecStart=/usr/bin/ceph-mds -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph + +[Install] +WantedBy=multi-user.target diff --git a/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mon.service b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mon.service new file mode 100644 index 000000000..d89c74a64 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-mon.service @@ -0,0 +1,22 @@ +[Unit] +Description=Ceph cluster monitor daemon +Documentation=man:ceph-mon + +After=network-online.target local-fs.target ceph-create-keys.service +Wants=network-online.target local-fs.target ceph-create-keys.service + +PartOf=ceph.target + +[Service] +LimitNOFILE=1048576 +LimitNPROC=1048576 +EnvironmentFile=-/etc/default/ceph +Environment=CLUSTER=ceph +ExecStart=/usr/bin/ceph-mon -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure +RestartSec=30 +TasksMax=infinity + +[Install] +WantedBy=multi-user.target diff --git a/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-osd@.service b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-osd@.service new file mode 100644 index 000000000..adfa6a02f --- /dev/null +++ b/ceph/ceph/debian/deb_folder/lib-systemd/system/ceph-osd@.service @@ -0,0 +1,22 @@ +[Unit] +Description=Ceph object storage daemon (OSD) +Documentation=man:ceph-osd +After=network-online.target +Wants=network-online.target +PartOf=ceph.service +RequiresMountsFor=/var/lib/ceph/osd/ceph-%i + +[Service] +Environment=CLUSTER=ceph +Environment=CONFIG=/etc/ceph/ceph.conf +EnvironmentFile=-/etc/default/ceph +ExecStartPre=-/bin/sh -c '${osd_prestart_sh}' -- %i +ExecStartPre=/usr/lib/ceph/ceph-osd-prestart.sh --id %i --cluster ${CLUSTER} +ExecStart=/usr/bin/ceph-osd --id %i --foreground --cluster ${CLUSTER} -c ${CONFIG} +ExecStopPost=-/bin/sh -c '${osd_poststop_sh}' -- %i +LimitNOFILE=327680 +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/ceph/ceph/debian/deb_folder/libcephfs-dev.install b/ceph/ceph/debian/deb_folder/libcephfs-dev.install new file mode 100644 index 000000000..a23c5b057 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libcephfs-dev.install @@ -0,0 +1,2 @@ +usr/include/cephfs/*.h +usr/lib/*/libcephfs.so diff --git a/ceph/ceph/debian/deb_folder/libcephfs-java.jlibs b/ceph/ceph/debian/deb_folder/libcephfs-java.jlibs new file mode 100644 index 000000000..1029fb53d --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libcephfs-java.jlibs @@ -0,0 +1 @@ +debian/tmp/usr/share/java/libcephfs.jar diff --git a/ceph/ceph/debian/deb_folder/libcephfs-jni.install b/ceph/ceph/debian/deb_folder/libcephfs-jni.install new file mode 100644 index 000000000..15cb91d90 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libcephfs-jni.install @@ -0,0 +1 @@ +usr/lib/*/libcephfs_jni.so* usr/lib/jni diff --git a/ceph/ceph/debian/deb_folder/libcephfs-jni.lintian-overrides b/ceph/ceph/debian/deb_folder/libcephfs-jni.lintian-overrides new file mode 100644 index 000000000..787d231db --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libcephfs-jni.lintian-overrides @@ -0,0 +1,2 @@ +custom-library-search-path usr/lib/jni/libcephfs_jni.so.1.0.0 /usr/lib/jvm/default-java/lib +custom-library-search-path usr/lib/jni/libcephfs_jni.so.1.0.0 /usr/lib/jvm/default-java/lib/server diff --git a/ceph/ceph/debian/deb_folder/libcephfs2.install b/ceph/ceph/debian/deb_folder/libcephfs2.install new file mode 100644 index 000000000..f09e93ca5 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libcephfs2.install @@ -0,0 +1 @@ +usr/lib/*/libcephfs.so.* diff --git a/ceph/ceph/debian/deb_folder/librados-dev.install b/ceph/ceph/debian/deb_folder/librados-dev.install new file mode 100644 index 000000000..edaa359fc --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librados-dev.install @@ -0,0 +1,5 @@ +usr/bin/librados-config +usr/include/rados/librados.h +usr/include/rados/rados_types.h +usr/lib/*/librados.so +usr/share/man/man8/librados-config.8 diff --git a/ceph/ceph/debian/deb_folder/librados2.install b/ceph/ceph/debian/deb_folder/librados2.install new file mode 100644 index 000000000..2020e29c4 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librados2.install @@ -0,0 +1,2 @@ +usr/lib/*/ceph/libceph-common.so* +usr/lib/*/librados.so.* diff --git a/ceph/ceph/debian/deb_folder/libradospp-dev.install b/ceph/ceph/debian/deb_folder/libradospp-dev.install new file mode 100644 index 000000000..749cdd772 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libradospp-dev.install @@ -0,0 +1,8 @@ +usr/include/rados/buffer.h +usr/include/rados/buffer_fwd.h +usr/include/rados/crc32c.h +usr/include/rados/inline_memory.h +usr/include/rados/librados.hpp +usr/include/rados/librados_fwd.hpp +usr/include/rados/page.h +usr/include/rados/rados_types.hpp diff --git a/ceph/ceph/debian/deb_folder/libradosstriper-dev.install b/ceph/ceph/debian/deb_folder/libradosstriper-dev.install new file mode 100644 index 000000000..4d6d7b5a0 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libradosstriper-dev.install @@ -0,0 +1,3 @@ +usr/include/radosstriper/libradosstriper.h +usr/include/radosstriper/libradosstriper.hpp +usr/lib/*/libradosstriper.so diff --git a/ceph/ceph/debian/deb_folder/libradosstriper1.install b/ceph/ceph/debian/deb_folder/libradosstriper1.install new file mode 100644 index 000000000..742549ba9 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/libradosstriper1.install @@ -0,0 +1 @@ +usr/lib/*/libradosstriper.so.* diff --git a/ceph/ceph/debian/deb_folder/librbd-dev.install b/ceph/ceph/debian/deb_folder/librbd-dev.install new file mode 100644 index 000000000..c6b455e58 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librbd-dev.install @@ -0,0 +1,4 @@ +usr/include/rbd/features.h +usr/include/rbd/librbd.h +usr/include/rbd/librbd.hpp +usr/lib/*/librbd.so diff --git a/ceph/ceph/debian/deb_folder/librbd1.install b/ceph/ceph/debian/deb_folder/librbd1.install new file mode 100644 index 000000000..decadbc19 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librbd1.install @@ -0,0 +1 @@ +usr/lib/*/librbd.so.* diff --git a/ceph/ceph/debian/deb_folder/librgw-dev.install b/ceph/ceph/debian/deb_folder/librgw-dev.install new file mode 100644 index 000000000..1fff6e4f0 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librgw-dev.install @@ -0,0 +1,5 @@ +usr/include/rados/librgw.h +usr/include/rados/librgw_admin_user.h +usr/include/rados/rgw_file.h +usr/lib/*/librgw.so +usr/lib/*/librgw_admin_user.so diff --git a/ceph/ceph/debian/deb_folder/librgw2.install b/ceph/ceph/debian/deb_folder/librgw2.install new file mode 100644 index 000000000..c6fd3ab59 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/librgw2.install @@ -0,0 +1,2 @@ +usr/lib/*/librgw.so.* +usr/lib/*/librgw_admin_user.so.* diff --git a/ceph/ceph/debian/deb_folder/man/ceph-crush-location.1 b/ceph/ceph/debian/deb_folder/man/ceph-crush-location.1 new file mode 100644 index 000000000..d05d85c7b --- /dev/null +++ b/ceph/ceph/debian/deb_folder/man/ceph-crush-location.1 @@ -0,0 +1,24 @@ +.TH ceph-crush-location "1" "April 2014" "ceph-crush-location" "User Commands" +.SH NAME +ceph-crush-location \- get CRUSH location +.SH DESCRIPTION +Generate a CRUSH location for the given entity + +The CRUSH location consists of a list of key=value pairs, separated +by spaces, all on a single line. This describes where in CRUSH +hierarhcy this entity should be placed. + +.SH OPTIONS +.TP 4 +\fB\-\-cluster\fR +name of the cluster (see /etc/ceph/$cluster.conf) +.TP 4 +\fB\-\-type\fR +daemon/entity type +.TP 4 +\fB\-\-id\fR +id (osd number, mds name, client name) + +.SH SEE ALSO +.TP +\fBceph-conf\fP(8) diff --git a/ceph/ceph/debian/deb_folder/man/mount.fuse.ceph.8 b/ceph/ceph/debian/deb_folder/man/mount.fuse.ceph.8 new file mode 100644 index 000000000..718936578 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/man/mount.fuse.ceph.8 @@ -0,0 +1,30 @@ +.TH mount.fuse.ceph "8" "March 2014" "ceph-fuse" "User Commands" +.SH NAME +mount.fuse.ceph \- wrapper around ceph-fuse +.SH DESCRIPTION +Helper to mount ceph-fuse from /etc/fstab. To use, add an entry like: + +.nf +# DEVICE PATH TYPE OPTIONS + mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0 + mount.fuse.ceph#conf=/etc/ceph/foo.conf,id=myuser /mnt/ceph2 fuse _netdev,noatime,allow_other 0 0 +.fi + +where the device field is a comma-separated list of options to pass on +the command line. The examples above, for example, specify that +ceph-fuse will authenticated as client.admin and client.myuser +(respectively), and the second example also sets the "conf" option to +"/etc/ceph/foo.conf" via the ceph-fuse command line. Any valid +ceph-fuse option can be passed in this way. + +.SH OPTIONS +.TP 4 +\fB\-\-conf\fR +path to ceph cponfiguration file, usually "/etc/ceph/ceph.conf" +.TP 4 +\fB\-\-id\fR +user name + +.SH SEE ALSO +.TP +\fBceph-fuse\fP(8) diff --git a/ceph/ceph/debian/deb_folder/missing-sources/bootstrap.js b/ceph/ceph/debian/deb_folder/missing-sources/bootstrap.js new file mode 100644 index 000000000..8a2e99a53 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/missing-sources/bootstrap.js @@ -0,0 +1,2377 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.7 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.3.7 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.7' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector === '#' ? [] : selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.3.7 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.7' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state += 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d).prop(d, true) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d).prop(d, false) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked')) changed = false + $parent.find('.active').removeClass('active') + this.$element.addClass('active') + } else if ($input.prop('type') == 'checkbox') { + if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false + this.$element.toggleClass('active') + } + $input.prop('checked', this.$element.hasClass('active')) + if (changed) $input.trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + this.$element.toggleClass('active') + } + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target).closest('.btn') + Plugin.call($btn, 'toggle') + if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { + // Prevent double click on radios, and the double selections (so cancellation) on checkboxes + e.preventDefault() + // The target component still receive the focus + if ($btn.is('input,button')) $btn.trigger('focus') + else $btn.find('input:visible,button:visible').first().trigger('focus') + } + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.3.7 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = null + this.sliding = null + this.interval = null + this.$active = null + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.7' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.3.7 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + +/* jshint latedef: false */ + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.7' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.7 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.7' + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $this = $(this) + var $parent = getParent($this) + var relatedTarget = { relatedTarget: this } + + if (!$parent.hasClass('open')) return + + if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return + + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') + $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) + }) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $(document.createElement('div')) + .addClass('dropdown-backdrop') + .insertAfter($(this)) + .on('click', clearMenus) + } + + var relatedTarget = { relatedTarget: this } + $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this + .trigger('focus') + .attr('aria-expanded', 'true') + + $parent + .toggleClass('open') + .trigger($.Event('shown.bs.dropdown', relatedTarget)) + } + + return false + } + + Dropdown.prototype.keydown = function (e) { + if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return + + var $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + if (!isActive && e.which != 27 || isActive && e.which == 27) { + if (e.which == 27) $parent.find(toggle).trigger('focus') + return $this.trigger('click') + } + + var desc = ' li:not(.disabled):visible a' + var $items = $parent.find('.dropdown-menu' + desc) + + if (!$items.length) return + + var index = $items.index(e.target) + + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq(index).trigger('focus') + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.dropdown') + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.dropdown + + $.fn.dropdown = Plugin + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $(document) + .on('click.bs.dropdown.data-api', clearMenus) + .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: modal.js v3.3.7 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$dialog = this.$element.find('.modal-dialog') + this.$backdrop = null + this.isShown = null + this.originalBodyPad = null + this.scrollbarWidth = 0 + this.ignoreBackdropClick = false + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.3.7' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + + this.escape() + this.resize() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.$dialog.on('mousedown.dismiss.bs.modal', function () { + that.$element.one('mouseup.dismiss.bs.modal', function (e) { + if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true + }) + }) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + that.adjustDialog() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$dialog // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + this.resize() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .off('click.dismiss.bs.modal') + .off('mouseup.dismiss.bs.modal') + + this.$dialog.off('mousedown.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (document !== e.target && + this.$element[0] !== e.target && + !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } + } + + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $(document.createElement('div')) + .addClass('modal-backdrop ' + animate) + .appendTo(this.$body) + + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (this.ignoreBackdropClick) { + this.ignoreBackdropClick = false + return + } + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus() + : this.hide() + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + // these following methods are used to handle overflowing modals + + Modal.prototype.handleUpdate = function () { + this.adjustDialog() + } + + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight + + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + var fullWindowWidth = window.innerWidth + if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 + var documentElementRect = document.documentElement.getBoundingClientRect() + fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) + } + this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth + this.scrollbarWidth = this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + this.originalBodyPad = document.body.style.paddingRight || '' + if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', this.originalBodyPad) + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.7 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = null + this.options = null + this.enabled = null + this.timeout = null + this.hoverState = null + this.$element = null + this.inState = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.7' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + container: false, + viewport: { + selector: 'body', + padding: 0 + } + } + + Tooltip.prototype.init = function (type, element, options) { + this.enabled = true + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) + this.inState = { click: false, hover: false, focus: false } + + if (this.$element[0] instanceof document.constructor && !this.options.selector) { + throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') + } + + var triggers = this.options.trigger.split(' ') + + for (var i = triggers.length; i--;) { + var trigger = triggers[i] + + if (trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (trigger != 'manual') { + var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' + var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' + + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + Tooltip.prototype.getDefaults = function () { + return Tooltip.DEFAULTS + } + + Tooltip.prototype.getOptions = function (options) { + options = $.extend({}, this.getDefaults(), this.$element.data(), options) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay, + hide: options.delay + } + } + + return options + } + + Tooltip.prototype.getDelegateOptions = function () { + var options = {} + var defaults = this.getDefaults() + + this._options && $.each(this._options, function (key, value) { + if (defaults[key] != value) options[key] = value + }) + + return options + } + + Tooltip.prototype.enter = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true + } + + if (self.tip().hasClass('in') || self.hoverState == 'in') { + self.hoverState = 'in' + return + } + + clearTimeout(self.timeout) + + self.hoverState = 'in' + + if (!self.options.delay || !self.options.delay.show) return self.show() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + Tooltip.prototype.isInStateTrue = function () { + for (var key in this.inState) { + if (this.inState[key]) return true + } + + return false + } + + Tooltip.prototype.leave = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false + } + + if (self.isInStateTrue()) return + + clearTimeout(self.timeout) + + self.hoverState = 'out' + + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + Tooltip.prototype.show = function () { + var e = $.Event('show.bs.' + this.type) + + if (this.hasContent() && this.enabled) { + this.$element.trigger(e) + + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) + if (e.isDefaultPrevented() || !inDom) return + var that = this + + var $tip = this.tip() + + var tipId = this.getUID(this.type) + + this.setContent() + $tip.attr('id', tipId) + this.$element.attr('aria-describedby', tipId) + + if (this.options.animation) $tip.addClass('fade') + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + var autoToken = /\s?auto?\s?/i + var autoPlace = autoToken.test(placement) + if (autoPlace) placement = placement.replace(autoToken, '') || 'top' + + $tip + .detach() + .css({ top: 0, left: 0, display: 'block' }) + .addClass(placement) + .data('bs.' + this.type, this) + + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) + this.$element.trigger('inserted.bs.' + this.type) + + var pos = this.getPosition() + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (autoPlace) { + var orgPlacement = placement + var viewportDim = this.getPosition(this.$viewport) + + placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : + placement + + $tip + .removeClass(orgPlacement) + .addClass(placement) + } + + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) + + this.applyPlacement(calculatedOffset, placement) + + var complete = function () { + var prevHoverState = that.hoverState + that.$element.trigger('shown.bs.' + that.type) + that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + } + } + + Tooltip.prototype.applyPlacement = function (offset, placement) { + var $tip = this.tip() + var width = $tip[0].offsetWidth + var height = $tip[0].offsetHeight + + // manually read margins because getBoundingClientRect includes difference + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top += marginTop + offset.left += marginLeft + + // $.fn.offset doesn't round pixel values + // so we use setOffset directly with our own function B-0 + $.offset.setOffset($tip[0], $.extend({ + using: function (props) { + $tip.css({ + top: Math.round(props.top), + left: Math.round(props.left) + }) + } + }, offset), 0) + + $tip.addClass('in') + + // check to see if placing tip in new offset caused the tip to resize itself + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (placement == 'top' && actualHeight != height) { + offset.top = offset.top + height - actualHeight + } + + var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) + + if (delta.left) offset.left += delta.left + else offset.top += delta.top + + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' + + $tip.offset(offset) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) + } + + Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { + this.arrow() + .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isVertical ? 'top' : 'left', '') + } + + Tooltip.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + Tooltip.prototype.hide = function (callback) { + var that = this + var $tip = $(this.$tip) + var e = $.Event('hide.bs.' + this.type) + + function complete() { + if (that.hoverState != 'in') $tip.detach() + if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + } + callback && callback() + } + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + $tip.removeClass('in') + + $.support.transition && $tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + + this.hoverState = null + + return this + } + + Tooltip.prototype.fixTitle = function () { + var $e = this.$element + if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') + } + } + + Tooltip.prototype.hasContent = function () { + return this.getTitle() + } + + Tooltip.prototype.getPosition = function ($element) { + $element = $element || this.$element + + var el = $element[0] + var isBody = el.tagName == 'BODY' + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var isSvg = window.SVGElement && el instanceof window.SVGElement + // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. + // See https://github.com/twbs/bootstrap/issues/20280 + var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) + } + + Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + + } + + Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { + var delta = { top: 0, left: 0 } + if (!this.$viewport) return delta + + var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 + var viewportDimensions = this.getPosition(this.$viewport) + + if (/right|left/.test(placement)) { + var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll + var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight + if (topEdgeOffset < viewportDimensions.top) { // top overflow + delta.top = viewportDimensions.top - topEdgeOffset + } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow + delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset + } + } else { + var leftEdgeOffset = pos.left - viewportPadding + var rightEdgeOffset = pos.left + viewportPadding + actualWidth + if (leftEdgeOffset < viewportDimensions.left) { // left overflow + delta.left = viewportDimensions.left - leftEdgeOffset + } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow + delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset + } + } + + return delta + } + + Tooltip.prototype.getTitle = function () { + var title + var $e = this.$element + var o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + Tooltip.prototype.getUID = function (prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + } + + Tooltip.prototype.tip = function () { + if (!this.$tip) { + this.$tip = $(this.options.template) + if (this.$tip.length != 1) { + throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') + } + } + return this.$tip + } + + Tooltip.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) + } + + Tooltip.prototype.enable = function () { + this.enabled = true + } + + Tooltip.prototype.disable = function () { + this.enabled = false + } + + Tooltip.prototype.toggleEnabled = function () { + this.enabled = !this.enabled + } + + Tooltip.prototype.toggle = function (e) { + var self = this + if (e) { + self = $(e.currentTarget).data('bs.' + this.type) + if (!self) { + self = new this.constructor(e.currentTarget, this.getDelegateOptions()) + $(e.currentTarget).data('bs.' + this.type, self) + } + } + + if (e) { + self.inState.click = !self.inState.click + if (self.isInStateTrue()) self.enter(self) + else self.leave(self) + } else { + self.tip().hasClass('in') ? self.leave(self) : self.enter(self) + } + } + + Tooltip.prototype.destroy = function () { + var that = this + clearTimeout(this.timeout) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + if (that.$tip) { + that.$tip.detach() + } + that.$tip = null + that.$arrow = null + that.$viewport = null + that.$element = null + }) + } + + + // TOOLTIP PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tooltip + + $.fn.tooltip = Plugin + $.fn.tooltip.Constructor = Tooltip + + + // TOOLTIP NO CONFLICT + // =================== + + $.fn.tooltip.noConflict = function () { + $.fn.tooltip = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: popover.js v3.3.7 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // POPOVER PUBLIC CLASS DEFINITION + // =============================== + + var Popover = function (element, options) { + this.init('popover', element, options) + } + + if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') + + Popover.VERSION = '3.3.7' + + Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }) + + + // NOTE: POPOVER EXTENDS tooltip.js + // ================================ + + Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) + + Popover.prototype.constructor = Popover + + Popover.prototype.getDefaults = function () { + return Popover.DEFAULTS + } + + Popover.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + var content = this.getContent() + + $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events + this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' + ](content) + + $tip.removeClass('fade top bottom left right in') + + // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do + // this manually by checking the contents. + if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() + } + + Popover.prototype.hasContent = function () { + return this.getTitle() || this.getContent() + } + + Popover.prototype.getContent = function () { + var $e = this.$element + var o = this.options + + return $e.attr('data-content') + || (typeof o.content == 'function' ? + o.content.call($e[0]) : + o.content) + } + + Popover.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.arrow')) + } + + + // POPOVER PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.popover + + $.fn.popover = Plugin + $.fn.popover.Constructor = Popover + + + // POPOVER NO CONFLICT + // =================== + + $.fn.popover.noConflict = function () { + $.fn.popover = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: scrollspy.js v3.3.7 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + this.$body = $(document.body) + this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.3.7' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var that = this + var offsetMethod = 'offset' + var offsetBase = 0 + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(href) + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + that.offsets.push(this[0]) + that.targets.push(this[1]) + }) + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + this.clear() + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tab.js v3.3.7 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TAB CLASS DEFINITION + // ==================== + + var Tab = function (element) { + // jscs:disable requireDollarBeforejQueryAssignment + this.element = $(element) + // jscs:enable requireDollarBeforejQueryAssignment + } + + Tab.VERSION = '3.3.7' + + Tab.TRANSITION_DURATION = 150 + + Tab.prototype.show = function () { + var $this = this.element + var $ul = $this.closest('ul:not(.dropdown-menu)') + var selector = $this.data('target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + if ($this.parent('li').hasClass('active')) return + + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] + }) + + $previous.trigger(hideEvent) + $this.trigger(showEvent) + + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return + + var $target = $(selector) + + this.activate($this.closest('li'), $ul) + this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) + $this.trigger({ + type: 'shown.bs.tab', + relatedTarget: $previous[0] + }) + }) + } + + Tab.prototype.activate = function (element, container, callback) { + var $active = container.find('> .active') + var transition = callback + && $.support.transition + && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) + + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if (element.parent('.dropdown-menu').length) { + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + } + + callback && callback() + } + + $active.length && transition ? + $active + .one('bsTransitionEnd', next) + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : + next() + + $active.removeClass('in') + } + + + // TAB PLUGIN DEFINITION + // ===================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tab') + + if (!data) $this.data('bs.tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tab + + $.fn.tab = Plugin + $.fn.tab.Constructor = Tab + + + // TAB NO CONFLICT + // =============== + + $.fn.tab.noConflict = function () { + $.fn.tab = old + return this + } + + + // TAB DATA-API + // ============ + + var clickHandler = function (e) { + e.preventDefault() + Plugin.call($(this), 'show') + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: affix.js v3.3.7 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = null + this.unpin = null + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.3.7' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && scrollTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var height = this.$element.height() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + var scrollHeight = Math.max($(document).height(), $(document.body).height()) + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) + + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - height - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/ceph/ceph/debian/deb_folder/missing-sources/two.js b/ceph/ceph/debian/deb_folder/missing-sources/two.js new file mode 100644 index 000000000..859d3b418 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/missing-sources/two.js @@ -0,0 +1,10244 @@ +/** + * two.js + * a two-dimensional drawing api meant for modern browsers. It is renderer + * agnostic enabling the same api for rendering in multiple contexts: webgl, + * canvas2d, and svg. + * + * Copyright (c) 2012 - 2017 jonobr1 / http://jonobr1.com + * + * 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. + * + */ + +this.Two = (function(previousTwo) { + + var root = typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : null; + var toString = Object.prototype.toString; + var _ = { + // http://underscorejs.org/ • 1.8.3 + _indexAmount: 0, + natural: { + slice: Array.prototype.slice, + indexOf: Array.prototype.indexOf, + keys: Object.keys, + bind: Function.prototype.bind, + create: Object.create + }, + identity: function(value) { + return value; + }, + isArguments: function(obj) { + return toString.call(obj) === '[object Arguments]'; + }, + isFunction: function(obj) { + return toString.call(obj) === '[object Function]'; + }, + isString: function(obj) { + return toString.call(obj) === '[object String]'; + }, + isNumber: function(obj) { + return toString.call(obj) === '[object Number]'; + }, + isDate: function(obj) { + return toString.call(obj) === '[object Date]'; + }, + isRegExp: function(obj) { + return toString.call(obj) === '[object RegExp]'; + }, + isError: function(obj) { + return toString.call(obj) === '[object Error]'; + }, + isFinite: function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }, + isNaN: function(obj) { + return _.isNumber(obj) && obj !== +obj; + }, + isBoolean: function(obj) { + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + }, + isNull: function(obj) { + return obj === null; + }, + isUndefined: function(obj) { + return obj === void 0; + }, + isEmpty: function(obj) { + if (obj == null) return true; + if (isArrayLike && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; + return _.keys(obj).length === 0; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isArray: Array.isArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }, + isObject: function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }, + toArray: function(obj) { + if (!obj) { + return []; + } + if (_.isArray(obj)) { + return slice.call(obj); + } + if (isArrayLike(obj)) { + return _.map(obj, _.identity); + } + return _.values(obj); + }, + range: function(start, stop, step) { + if (stop == null) { + stop = start || 0; + start = 0; + } + step = step || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = Array(length); + + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; + } + + return range; + }, + indexOf: function(list, item) { + if (!!_.natural.indexOf) { + return _.natural.indexOf.call(list, item); + } + for (var i = 0; i < list.length; i++) { + if (list[i] === item) { + return i; + } + } + return -1; + }, + has: function(obj, key) { + return obj != null && hasOwnProperty.call(obj, key); + }, + bind: function(func, ctx) { + var natural = _.natural.bind; + if (natural && func.bind === natural) { + return natural.apply(func, slice.call(arguments, 1)); + } + var args = slice.call(arguments, 2); + return function() { + func.apply(ctx, args); + }; + }, + extend: function(base) { + var sources = slice.call(arguments, 1); + for (var i = 0; i < sources.length; i++) { + var obj = sources[i]; + for (var k in obj) { + base[k] = obj[k]; + } + } + return base; + }, + defaults: function(base) { + var sources = slice.call(arguments, 1); + for (var i = 0; i < sources.length; i++) { + var obj = sources[i]; + for (var k in obj) { + if (base[k] === void 0) { + base[k] = obj[k]; + } + } + } + return base; + }, + keys: function(obj) { + if (!_.isObject(obj)) { + return []; + } + if (_.natural.keys) { + return _.natural.keys(obj); + } + var keys = []; + for (var k in obj) { + if (_.has(obj, k)) { + keys.push(k); + } + } + return keys; + }, + values: function(obj) { + var keys = _.keys(obj); + var values = []; + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + values.push(obj[k]); + } + return values; + }, + each: function(obj, iteratee, context) { + var ctx = context || this; + var keys = !isArrayLike(obj) && _.keys(obj); + var length = (keys || obj).length; + for (var i = 0; i < length; i++) { + var k = keys ? keys[i] : i; + iteratee.call(ctx, obj[k], k, obj); + } + return obj; + }, + map: function(obj, iteratee, context) { + var ctx = context || this; + var keys = !isArrayLike(obj) && _.keys(obj); + var length = (keys || obj).length; + var result = []; + for (var i = 0; i < length; i++) { + var k = keys ? keys[i] : i; + result[i] = iteratee.call(ctx, obj[k], k, obj); + } + return result; + }, + once: function(func) { + var init = false; + return function() { + if (!!init) { + return func; + } + init = true; + return func.apply(this, arguments); + } + }, + after: function(times, func) { + return function() { + while (--times < 1) { + return func.apply(this, arguments); + } + } + }, + uniqueId: function(prefix) { + var id = ++_._indexAmount + ''; + return prefix ? prefix + id : id; + } + }; + + /** + * Constants + */ + + var sin = Math.sin, + cos = Math.cos, + atan2 = Math.atan2, + sqrt = Math.sqrt, + round = Math.round, + abs = Math.abs, + PI = Math.PI, + TWO_PI = PI * 2, + HALF_PI = PI / 2, + pow = Math.pow, + min = Math.min, + max = Math.max; + + /** + * Localized variables + */ + + var count = 0; + var slice = _.natural.slice; + var perf = ((root.performance && root.performance.now) ? root.performance : Date); + var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; + var getLength = function(obj) { + return obj == null ? void 0 : obj['length']; + }; + var isArrayLike = function(collection) { + var length = getLength(collection); + return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; + }; + + /** + * Cross browser dom events. + */ + var dom = { + + temp: (root.document ? root.document.createElement('div') : {}), + + hasEventListeners: _.isFunction(root.addEventListener), + + bind: function(elem, event, func, bool) { + if (this.hasEventListeners) { + elem.addEventListener(event, func, !!bool); + } else { + elem.attachEvent('on' + event, func); + } + return dom; + }, + + unbind: function(elem, event, func, bool) { + if (dom.hasEventListeners) { + elem.removeEventListeners(event, func, !!bool); + } else { + elem.detachEvent('on' + event, func); + } + return dom; + }, + + getRequestAnimationFrame: function() { + + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + var request = root.requestAnimationFrame, cancel; + + if(!request) { + for (var i = 0; i < vendors.length; i++) { + request = root[vendors[i] + 'RequestAnimationFrame'] || request; + cancel = root[vendors[i] + 'CancelAnimationFrame'] + || root[vendors[i] + 'CancelRequestAnimationFrame'] || cancel; + } + + request = request || function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = root.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + // cancel = cancel || function(id) { + // clearTimeout(id); + // }; + } + + request.init = _.once(loop); + + return request; + + } + + }; + + /** + * @class + */ + var Two = root.Two = function(options) { + + // Determine what Renderer to use and setup a scene. + + var params = _.defaults(options || {}, { + fullscreen: false, + width: 640, + height: 480, + type: Two.Types.svg, + autostart: false + }); + + _.each(params, function(v, k) { + if (k === 'fullscreen' || k === 'autostart') { + return; + } + this[k] = v; + }, this); + + // Specified domElement overrides type declaration only if the element does not support declared renderer type. + if (_.isElement(params.domElement)) { + var tagName = params.domElement.tagName.toLowerCase(); + // TODO: Reconsider this if statement's logic. + if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(this.type+'-'+tagName)) { + this.type = Two.Types[tagName]; + } + } + + this.renderer = new Two[this.type](this); + Two.Utils.setPlaying.call(this, params.autostart); + this.frameCount = 0; + + if (params.fullscreen) { + + var fitted = _.bind(fitToWindow, this); + _.extend(document.body.style, { + overflow: 'hidden', + margin: 0, + padding: 0, + top: 0, + left: 0, + right: 0, + bottom: 0, + position: 'fixed' + }); + _.extend(this.renderer.domElement.style, { + display: 'block', + top: 0, + left: 0, + right: 0, + bottom: 0, + position: 'fixed' + }); + dom.bind(root, 'resize', fitted); + fitted(); + + + } else if (!_.isElement(params.domElement)) { + + this.renderer.setSize(params.width, params.height, this.ratio); + this.width = params.width; + this.height = params.height; + + } + + this.scene = this.renderer.scene; + + Two.Instances.push(this); + raf.init(); + + }; + + _.extend(Two, { + + /** + * Access to root in other files. + */ + + root: root, + + /** + * Primitive + */ + + Array: root.Float32Array || Array, + + Types: { + webgl: 'WebGLRenderer', + svg: 'SVGRenderer', + canvas: 'CanvasRenderer' + }, + + Version: 'v0.7.0', + + Identifier: 'two_', + + Properties: { + hierarchy: 'hierarchy', + demotion: 'demotion' + }, + + Events: { + play: 'play', + pause: 'pause', + update: 'update', + render: 'render', + resize: 'resize', + change: 'change', + remove: 'remove', + insert: 'insert', + order: 'order', + load: 'load' + }, + + Commands: { + move: 'M', + line: 'L', + curve: 'C', + close: 'Z' + }, + + Resolution: 8, + + Instances: [], + + noConflict: function() { + root.Two = previousTwo; + return this; + }, + + uniqueId: function() { + var id = count; + count++; + return id; + }, + + Utils: _.extend(_, { + + performance: perf, + + defineProperty: function(property) { + + var object = this; + var secret = '_' + property; + var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1); + + Object.defineProperty(object, property, { + enumerable: true, + get: function() { + return this[secret]; + }, + set: function(v) { + this[secret] = v; + this[flag] = true; + } + }); + + }, + + /** + * Release an arbitrary class' events from the two.js corpus and recurse + * through its children and or vertices. + */ + release: function(obj) { + + if (!_.isObject(obj)) { + return; + } + + if (_.isFunction(obj.unbind)) { + obj.unbind(); + } + + if (obj.vertices) { + if (_.isFunction(obj.vertices.unbind)) { + obj.vertices.unbind(); + } + _.each(obj.vertices, function(v) { + if (_.isFunction(v.unbind)) { + v.unbind(); + } + }); + } + + if (obj.children) { + _.each(obj.children, function(obj) { + Two.Utils.release(obj); + }); + } + + }, + + xhr: function(path, callback) { + + var xhr = new XMLHttpRequest(); + xhr.open('GET', path); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4 && xhr.status === 200) { + callback(xhr.responseText); + } + }; + + xhr.send(); + return xhr; + + }, + + Curve: { + + CollinearityEpsilon: pow(10, -30), + + RecursionLimit: 16, + + CuspLimit: 0, + + Tolerance: { + distance: 0.25, + angle: 0, + epsilon: 0.01 + }, + + // Lookup tables for abscissas and weights with values for n = 2 .. 16. + // As values are symmetric, only store half of them and adapt algorithm + // to factor in symmetry. + abscissas: [ + [ 0.5773502691896257645091488], + [0,0.7745966692414833770358531], + [ 0.3399810435848562648026658,0.8611363115940525752239465], + [0,0.5384693101056830910363144,0.9061798459386639927976269], + [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], + [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], + [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], + [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], + [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], + [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], + [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], + [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], + [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], + [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], + [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] + ], + + weights: [ + [1], + [0.8888888888888888888888889,0.5555555555555555555555556], + [0.6521451548625461426269361,0.3478548451374538573730639], + [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], + [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961], + [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], + [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], + [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], + [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], + [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], + [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], + [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], + [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], + [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], + [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] + ] + + }, + + /** + * Account for high dpi rendering. + * http://www.html5rocks.com/en/tutorials/canvas/hidpi/ + */ + + devicePixelRatio: root.devicePixelRatio || 1, + + getBackingStoreRatio: function(ctx) { + return ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1; + }, + + getRatio: function(ctx) { + return Two.Utils.devicePixelRatio / getBackingStoreRatio(ctx); + }, + + /** + * Properly defer play calling until after all objects + * have been updated with their newest styles. + */ + setPlaying: function(b) { + + this.playing = !!b; + return this; + + }, + + /** + * Return the computed matrix of a nested object. + * TODO: Optimize traversal. + */ + getComputedMatrix: function(object, matrix) { + + matrix = (matrix && matrix.identity()) || new Two.Matrix(); + var parent = object, matrices = []; + + while (parent && parent._matrix) { + matrices.push(parent._matrix); + parent = parent.parent; + } + + matrices.reverse(); + + _.each(matrices, function(m) { + + var e = m.elements; + matrix.multiply( + e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9]); + + }); + + return matrix; + + }, + + deltaTransformPoint: function(matrix, x, y) { + + var dx = x * matrix.a + y * matrix.c + 0; + var dy = x * matrix.b + y * matrix.d + 0; + + return new Two.Vector(dx, dy); + + }, + + /** + * https://gist.github.com/2052247 + */ + decomposeMatrix: function(matrix) { + + // calculate delta transform point + var px = Two.Utils.deltaTransformPoint(matrix, 0, 1); + var py = Two.Utils.deltaTransformPoint(matrix, 1, 0); + + // calculate skew + var skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90); + var skewY = ((180 / Math.PI) * Math.atan2(py.y, py.x)); + + return { + translateX: matrix.e, + translateY: matrix.f, + scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b), + scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d), + skewX: skewX, + skewY: skewY, + rotation: skewX // rotation is the same as skew x + }; + + }, + + /** + * Walk through item properties and pick the ones of interest. + * Will try to resolve styles applied via CSS + * + * TODO: Reverse calculate `Two.Gradient`s for fill / stroke + * of any given path. + */ + applySvgAttributes: function(node, elem) { + + var attributes = {}, styles = {}, i, key, value, attr; + + // Not available in non browser environments + if (getComputedStyle) { + // Convert CSSStyleDeclaration to a normal object + var computedStyles = getComputedStyle(node); + i = computedStyles.length; + + while (i--) { + key = computedStyles[i]; + value = computedStyles[key]; + // Gecko returns undefined for unset properties + // Webkit returns the default value + if (value !== undefined) { + styles[key] = value; + } + } + } + + // Convert NodeMap to a normal object + i = node.attributes.length; + while (i--) { + attr = node.attributes[i]; + attributes[attr.nodeName] = attr.value; + } + + // Getting the correct opacity is a bit tricky, since SVG path elements don't + // support opacity as an attribute, but you can apply it via CSS. + // So we take the opacity and set (stroke/fill)-opacity to the same value. + if (!_.isUndefined(styles.opacity)) { + styles['stroke-opacity'] = styles.opacity; + styles['fill-opacity'] = styles.opacity; + } + + // Merge attributes and applied styles (attributes take precedence) + _.extend(styles, attributes); + + // Similarly visibility is influenced by the value of both display and visibility. + // Calculate a unified value here which defaults to `true`. + styles.visible = !(_.isUndefined(styles.display) && styles.display === 'none') + || (_.isUndefined(styles.visibility) && styles.visibility === 'hidden'); + + // Now iterate the whole thing + for (key in styles) { + value = styles[key]; + + switch (key) { + case 'transform': + // TODO: Check this out https://github.com/paperjs/paper.js/blob/master/src/svg/SVGImport.js#L313 + if (value === 'none') break; + var m = node.getCTM ? node.getCTM() : null; + + // Might happen when transform string is empty or not valid. + if (m === null) break; + + // // Option 1: edit the underlying matrix and don't force an auto calc. + // var m = node.getCTM(); + // elem._matrix.manual = true; + // elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f); + + // Option 2: Decompose and infer Two.js related properties. + var transforms = Two.Utils.decomposeMatrix(node.getCTM()); + + elem.translation.set(transforms.translateX, transforms.translateY); + elem.rotation = transforms.rotation; + // Warning: Two.js elements only support uniform scalars... + elem.scale = transforms.scaleX; + + var x = parseFloat((styles.x + '').replace('px')); + var y = parseFloat((styles.y + '').replace('px')); + + // Override based on attributes. + if (x) { + elem.translation.x = x; + } + + if (y) { + elem.translation.y = y; + } + + break; + case 'visible': + elem.visible = value; + break; + case 'stroke-linecap': + elem.cap = value; + break; + case 'stroke-linejoin': + elem.join = value; + break; + case 'stroke-miterlimit': + elem.miter = value; + break; + case 'stroke-width': + elem.linewidth = parseFloat(value); + break; + case 'stroke-opacity': + case 'fill-opacity': + case 'opacity': + elem.opacity = parseFloat(value); + break; + case 'fill': + case 'stroke': + if (/url\(\#.*\)/i.test(value)) { + elem[key] = this.getById( + value.replace(/url\(\#(.*)\)/i, '$1')); + } else { + elem[key] = (value === 'none') ? 'transparent' : value; + } + break; + case 'id': + elem.id = value; + break; + case 'class': + elem.classList = value.split(' '); + break; + } + } + + return elem; + + }, + + /** + * Read any number of SVG node types and create Two equivalents of them. + */ + read: { + + svg: function() { + return Two.Utils.read.g.apply(this, arguments); + }, + + g: function(node) { + + var group = new Two.Group(); + + // Switched up order to inherit more specific styles + Two.Utils.applySvgAttributes.call(this, node, group); + + for (var i = 0, l = node.childNodes.length; i < l; i++) { + var n = node.childNodes[i]; + var tag = n.nodeName; + if (!tag) return; + + var tagName = tag.replace(/svg\:/ig, '').toLowerCase(); + + if (tagName in Two.Utils.read) { + var o = Two.Utils.read[tagName].call(group, n); + group.add(o); + } + } + + return group; + + }, + + polygon: function(node, open) { + + var points = node.getAttribute('points'); + + var verts = []; + points.replace(/(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g, function(match, p1, p2) { + verts.push(new Two.Anchor(parseFloat(p1), parseFloat(p2))); + }); + + var poly = new Two.Path(verts, !open).noStroke(); + poly.fill = 'black'; + + return Two.Utils.applySvgAttributes.call(this, node, poly); + + }, + + polyline: function(node) { + return Two.Utils.read.polygon.call(this, node, true); + }, + + path: function(node) { + + var path = node.getAttribute('d'); + + // Create a Two.Path from the paths. + + var coord = new Two.Anchor(); + var control, coords; + var closed = false, relative = false; + var commands = path.match(/[a-df-z][^a-df-z]*/ig); + var last = commands.length - 1; + + // Split up polybeziers + + _.each(commands.slice(0), function(command, i) { + + var type = command[0]; + var lower = type.toLowerCase(); + var items = command.slice(1).trim().split(/[\s,]+|(?=\s?[+\-])/); + var pre, post, result = [], bin; + + if (i <= 0) { + commands = []; + } + + switch (lower) { + case 'h': + case 'v': + if (items.length > 1) { + bin = 1; + } + break; + case 'm': + case 'l': + case 't': + if (items.length > 2) { + bin = 2; + } + break; + case 's': + case 'q': + if (items.length > 4) { + bin = 4; + } + break; + case 'c': + if (items.length > 6) { + bin = 6; + } + break; + case 'a': + // TODO: Handle Ellipses + break; + } + + if (bin) { + + for (var j = 0, l = items.length, times = 0; j < l; j+=bin) { + + var ct = type; + if (times > 0) { + + switch (type) { + case 'm': + ct = 'l'; + break; + case 'M': + ct = 'L'; + break; + } + + } + + result.push([ct].concat(items.slice(j, j + bin)).join(' ')); + times++; + + } + + commands = Array.prototype.concat.apply(commands, result); + + } else { + + commands.push(command); + + } + + }); + + // Create the vertices for our Two.Path + + var points = []; + _.each(commands, function(command, i) { + + var result, x, y; + var type = command[0]; + var lower = type.toLowerCase(); + + coords = command.slice(1).trim(); + coords = coords.replace(/(-?\d+(?:\.\d*)?)[eE]([+\-]?\d+)/g, function(match, n1, n2) { + return parseFloat(n1) * pow(10, n2); + }); + coords = coords.split(/[\s,]+|(?=\s?[+\-])/); + relative = type === lower; + + var x1, y1, x2, y2, x3, y3, x4, y4, reflection; + + switch (lower) { + + case 'z': + if (i >= last) { + closed = true; + } else { + x = coord.x; + y = coord.y; + result = new Two.Anchor( + x, y, + undefined, undefined, + undefined, undefined, + Two.Commands.close + ); + } + break; + + case 'm': + case 'l': + + x = parseFloat(coords[0]); + y = parseFloat(coords[1]); + + result = new Two.Anchor( + x, y, + undefined, undefined, + undefined, undefined, + lower === 'm' ? Two.Commands.move : Two.Commands.line + ); + + if (relative) { + result.addSelf(coord); + } + + // result.controls.left.copy(result); + // result.controls.right.copy(result); + + coord = result; + break; + + case 'h': + case 'v': + + var a = lower === 'h' ? 'x' : 'y'; + var b = a === 'x' ? 'y' : 'x'; + + result = new Two.Anchor( + undefined, undefined, + undefined, undefined, + undefined, undefined, + Two.Commands.line + ); + result[a] = parseFloat(coords[0]); + result[b] = coord[b]; + + if (relative) { + result[a] += coord[a]; + } + + // result.controls.left.copy(result); + // result.controls.right.copy(result); + + coord = result; + break; + + case 'c': + case 's': + + x1 = coord.x; + y1 = coord.y; + + if (!control) { + control = new Two.Vector();//.copy(coord); + } + + if (lower === 'c') { + + x2 = parseFloat(coords[0]); + y2 = parseFloat(coords[1]); + x3 = parseFloat(coords[2]); + y3 = parseFloat(coords[3]); + x4 = parseFloat(coords[4]); + y4 = parseFloat(coords[5]); + + } else { + + // Calculate reflection control point for proper x2, y2 + // inclusion. + + reflection = getReflection(coord, control, relative); + + x2 = reflection.x; + y2 = reflection.y; + x3 = parseFloat(coords[0]); + y3 = parseFloat(coords[1]); + x4 = parseFloat(coords[2]); + y4 = parseFloat(coords[3]); + + } + + if (relative) { + x2 += x1; + y2 += y1; + x3 += x1; + y3 += y1; + x4 += x1; + y4 += y1; + } + + if (!_.isObject(coord.controls)) { + Two.Anchor.AppendCurveProperties(coord); + } + + coord.controls.right.set(x2 - coord.x, y2 - coord.y); + result = new Two.Anchor( + x4, y4, + x3 - x4, y3 - y4, + undefined, undefined, + Two.Commands.curve + ); + + coord = result; + control = result.controls.left; + + break; + + case 't': + case 'q': + + x1 = coord.x; + y1 = coord.y; + + if (!control) { + control = new Two.Vector();//.copy(coord); + } + + if (control.isZero()) { + x2 = x1; + y2 = y1; + } else { + x2 = control.x; + y1 = control.y; + } + + if (lower === 'q') { + + x3 = parseFloat(coords[0]); + y3 = parseFloat(coords[1]); + x4 = parseFloat(coords[1]); + y4 = parseFloat(coords[2]); + + } else { + + reflection = getReflection(coord, control, relative); + + x3 = reflection.x; + y3 = reflection.y; + x4 = parseFloat(coords[0]); + y4 = parseFloat(coords[1]); + + } + + if (relative) { + x2 += x1; + y2 += y1; + x3 += x1; + y3 += y1; + x4 += x1; + y4 += y1; + } + + if (!_.isObject(coord.controls)) { + Two.Anchor.AppendCurveProperties(coord); + } + + coord.controls.right.set(x2 - coord.x, y2 - coord.y); + result = new Two.Anchor( + x4, y4, + x3 - x4, y3 - y4, + undefined, undefined, + Two.Commands.curve + ); + + coord = result; + control = result.controls.left; + + break; + + case 'a': + + // throw new Two.Utils.Error('not yet able to interpret Elliptical Arcs.'); + x1 = coord.x; + y1 = coord.y; + + var rx = parseFloat(coords[0]); + var ry = parseFloat(coords[1]); + var xAxisRotation = parseFloat(coords[2]) * Math.PI / 180; + var largeArcFlag = parseFloat(coords[3]); + var sweepFlag = parseFloat(coords[4]); + + x4 = parseFloat(coords[5]); + y4 = parseFloat(coords[6]); + + if (relative) { + x4 += x1; + y4 += y1; + } + + // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter + + // Calculate midpoint mx my + var mx = (x4 - x1) / 2; + var my = (y4 - y1) / 2; + + // Calculate x1' y1' F.6.5.1 + var _x = mx * Math.cos(xAxisRotation) + my * Math.sin(xAxisRotation); + var _y = - mx * Math.sin(xAxisRotation) + my * Math.cos(xAxisRotation); + + var rx2 = rx * rx; + var ry2 = ry * ry; + var _x2 = _x * _x; + var _y2 = _y * _y; + + // adjust radii + var l = _x2 / rx2 + _y2 / ry2; + if (l > 1) { + rx *= Math.sqrt(l); + ry *= Math.sqrt(l); + } + + var amp = Math.sqrt((rx2 * ry2 - rx2 * _y2 - ry2 * _x2) / (rx2 * _y2 + ry2 * _x2)); + + if (_.isNaN(amp)) { + amp = 0; + } else if (largeArcFlag != sweepFlag && amp > 0) { + amp *= -1; + } + + // Calculate cx' cy' F.6.5.2 + var _cx = amp * rx * _y / ry; + var _cy = - amp * ry * _x / rx; + + // Calculate cx cy F.6.5.3 + var cx = _cx * Math.cos(xAxisRotation) - _cy * Math.sin(xAxisRotation) + (x1 + x4) / 2; + var cy = _cx * Math.sin(xAxisRotation) + _cy * Math.cos(xAxisRotation) + (y1 + y4) / 2; + + // vector magnitude + var m = function(v) { return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2)); } + // ratio between two vectors + var r = function(u, v) { return (u[0] * v[0] + u[1] * v[1]) / (m(u) * m(v)) } + // angle between two vectors + var a = function(u, v) { return (u[0] * v[1] < u[1] * v[0] ? - 1 : 1) * Math.acos(r(u,v)); } + + // Calculate theta1 and delta theta F.6.5.4 + F.6.5.5 + var t1 = a([1, 0], [(_x - _cx) / rx, (_y - _cy) / ry]); + var u = [(_x - _cx) / rx, (_y - _cy) / ry]; + var v = [( - _x - _cx) / rx, ( - _y - _cy) / ry]; + var dt = a(u, v); + + if (r(u, v) <= -1) dt = Math.PI; + if (r(u, v) >= 1) dt = 0; + + // F.6.5.6 + if (largeArcFlag) { + dt = mod(dt, Math.PI * 2); + } + + if (sweepFlag && dt > 0) { + dt -= Math.PI * 2; + } + + var length = Two.Resolution; + + // Save a projection of our rotation and translation to apply + // to the set of points. + var projection = new Two.Matrix() + .translate(cx, cy) + .rotate(xAxisRotation); + + // Create a resulting array of Two.Anchor's to export to the + // the path. + result = _.map(_.range(length), function(i) { + var pct = 1 - (i / (length - 1)); + var theta = pct * dt + t1; + var x = rx * Math.cos(theta); + var y = ry * Math.sin(theta); + var projected = projection.multiply(x, y, 1); + return new Two.Anchor(projected.x, projected.y, false, false, false, false, Two.Commands.line);; + }); + + result.push(new Two.Anchor(x4, y4, false, false, false, false, Two.Commands.line)); + + coord = result[result.length - 1]; + control = coord.controls.left; + + break; + + } + + if (result) { + if (_.isArray(result)) { + points = points.concat(result); + } else { + points.push(result); + } + } + + }); + + if (points.length <= 1) { + return; + } + + var path = new Two.Path(points, closed, undefined, true).noStroke(); + path.fill = 'black'; + + var rect = path.getBoundingClientRect(true); + + // Center objects to stay consistent + // with the rest of the Two.js API. + rect.centroid = { + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2 + }; + + _.each(path.vertices, function(v) { + v.subSelf(rect.centroid); + }); + + path.translation.addSelf(rect.centroid); + + return Two.Utils.applySvgAttributes.call(this, node, path); + + }, + + circle: function(node) { + + var x = parseFloat(node.getAttribute('cx')); + var y = parseFloat(node.getAttribute('cy')); + var r = parseFloat(node.getAttribute('r')); + + var circle = new Two.Circle(x, y, r).noStroke(); + circle.fill = 'black'; + + return Two.Utils.applySvgAttributes.call(this, node, circle); + + }, + + ellipse: function(node) { + + var x = parseFloat(node.getAttribute('cx')); + var y = parseFloat(node.getAttribute('cy')); + var width = parseFloat(node.getAttribute('rx')); + var height = parseFloat(node.getAttribute('ry')); + + var ellipse = new Two.Ellipse(x, y, width, height).noStroke(); + ellipse.fill = 'black'; + + return Two.Utils.applySvgAttributes.call(this, node, ellipse); + + }, + + rect: function(node) { + + var x = parseFloat(node.getAttribute('x')) || 0; + var y = parseFloat(node.getAttribute('y')) || 0; + var width = parseFloat(node.getAttribute('width')); + var height = parseFloat(node.getAttribute('height')); + + var w2 = width / 2; + var h2 = height / 2; + + var rect = new Two.Rectangle(x + w2, y + h2, width, height) + .noStroke(); + rect.fill = 'black'; + + return Two.Utils.applySvgAttributes.call(this, node, rect); + + }, + + line: function(node) { + + var x1 = parseFloat(node.getAttribute('x1')); + var y1 = parseFloat(node.getAttribute('y1')); + var x2 = parseFloat(node.getAttribute('x2')); + var y2 = parseFloat(node.getAttribute('y2')); + + var line = new Two.Line(x1, y1, x2, y2).noFill(); + + return Two.Utils.applySvgAttributes.call(this, node, line); + + }, + + lineargradient: function(node) { + + var x1 = parseFloat(node.getAttribute('x1')); + var y1 = parseFloat(node.getAttribute('y1')); + var x2 = parseFloat(node.getAttribute('x2')); + var y2 = parseFloat(node.getAttribute('y2')); + + var ox = (x2 + x1) / 2; + var oy = (y2 + y1) / 2; + + var stops = []; + for (var i = 0; i < node.children.length; i++) { + + var child = node.children[i]; + + var offset = parseFloat(child.getAttribute('offset')); + var color = child.getAttribute('stop-color'); + var opacity = child.getAttribute('stop-opacity'); + var style = child.getAttribute('style'); + + if (_.isNull(color)) { + var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false; + color = matches && matches.length > 1 ? matches[1] : undefined; + } + + if (_.isNull(opacity)) { + var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false; + opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; + } + + stops.push(new Two.Gradient.Stop(offset, color, opacity)); + + } + + var gradient = new Two.LinearGradient(x1 - ox, y1 - oy, x2 - ox, + y2 - oy, stops); + + return Two.Utils.applySvgAttributes.call(this, node, gradient); + + }, + + radialgradient: function(node) { + + var cx = parseFloat(node.getAttribute('cx')) || 0; + var cy = parseFloat(node.getAttribute('cy')) || 0; + var r = parseFloat(node.getAttribute('r')); + + var fx = parseFloat(node.getAttribute('fx')); + var fy = parseFloat(node.getAttribute('fy')); + + if (_.isNaN(fx)) { + fx = cx; + } + + if (_.isNaN(fy)) { + fy = cy; + } + + var ox = Math.abs(cx + fx) / 2; + var oy = Math.abs(cy + fy) / 2; + + var stops = []; + for (var i = 0; i < node.children.length; i++) { + + var child = node.children[i]; + + var offset = parseFloat(child.getAttribute('offset')); + var color = child.getAttribute('stop-color'); + var opacity = child.getAttribute('stop-opacity'); + var style = child.getAttribute('style'); + + if (_.isNull(color)) { + var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false; + color = matches && matches.length > 1 ? matches[1] : undefined; + } + + if (_.isNull(opacity)) { + var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false; + opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; + } + + stops.push(new Two.Gradient.Stop(offset, color, opacity)); + + } + + var gradient = new Two.RadialGradient(cx - ox, cy - oy, r, + stops, fx - ox, fy - oy); + + return Two.Utils.applySvgAttributes.call(this, node, gradient); + + } + + }, + + /** + * Given 2 points (a, b) and corresponding control point for each + * return an array of points that represent points plotted along + * the curve. Number points determined by limit. + */ + subdivide: function(x1, y1, x2, y2, x3, y3, x4, y4, limit) { + + limit = limit || Two.Utils.Curve.RecursionLimit; + var amount = limit + 1; + + // TODO: Issue 73 + // Don't recurse if the end points are identical + if (x1 === x4 && y1 === y4) { + return [new Two.Anchor(x4, y4)]; + } + + return _.map(_.range(0, amount), function(i) { + + var t = i / amount; + var x = getPointOnCubicBezier(t, x1, x2, x3, x4); + var y = getPointOnCubicBezier(t, y1, y2, y3, y4); + + return new Two.Anchor(x, y); + + }); + + }, + + getPointOnCubicBezier: function(t, a, b, c, d) { + var k = 1 - t; + return (k * k * k * a) + (3 * k * k * t * b) + (3 * k * t * t * c) + + (t * t * t * d); + }, + + /** + * Given 2 points (a, b) and corresponding control point for each + * return a float that represents the length of the curve using + * Gauss-Legendre algorithm. Limit iterations of calculation by `limit`. + */ + getCurveLength: function(x1, y1, x2, y2, x3, y3, x4, y4, limit) { + + // TODO: Better / fuzzier equality check + // Linear calculation + if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) { + var dx = x4 - x1; + var dy = y4 - y1; + return sqrt(dx * dx + dy * dy); + } + + // Calculate the coefficients of a Bezier derivative. + var ax = 9 * (x2 - x3) + 3 * (x4 - x1), + bx = 6 * (x1 + x3) - 12 * x2, + cx = 3 * (x2 - x1), + + ay = 9 * (y2 - y3) + 3 * (y4 - y1), + by = 6 * (y1 + y3) - 12 * y2, + cy = 3 * (y2 - y1); + + var integrand = function(t) { + // Calculate quadratic equations of derivatives for x and y + var dx = (ax * t + bx) * t + cx, + dy = (ay * t + by) * t + cy; + return sqrt(dx * dx + dy * dy); + }; + + return integrate( + integrand, 0, 1, limit || Two.Utils.Curve.RecursionLimit + ); + + }, + + /** + * Integration for `getCurveLength` calculations. Referenced from + * Paper.js: https://github.com/paperjs/paper.js/blob/master/src/util/Numerical.js#L101 + */ + integrate: function(f, a, b, n) { + var x = Two.Utils.Curve.abscissas[n - 2], + w = Two.Utils.Curve.weights[n - 2], + A = 0.5 * (b - a), + B = A + a, + i = 0, + m = (n + 1) >> 1, + sum = n & 1 ? w[i++] * f(B) : 0; // Handle odd n + while (i < m) { + var Ax = A * x[i]; + sum += w[i++] * (f(B + Ax) + f(B - Ax)); + } + return A * sum; + }, + + /** + * Creates a set of points that have u, v values for anchor positions + */ + getCurveFromPoints: function(points, closed) { + + var l = points.length, last = l - 1; + + for (var i = 0; i < l; i++) { + + var point = points[i]; + + if (!_.isObject(point.controls)) { + Two.Anchor.AppendCurveProperties(point); + } + + var prev = closed ? mod(i - 1, l) : max(i - 1, 0); + var next = closed ? mod(i + 1, l) : min(i + 1, last); + + var a = points[prev]; + var b = point; + var c = points[next]; + getControlPoints(a, b, c); + + b._command = i === 0 ? Two.Commands.move : Two.Commands.curve; + + b.controls.left.x = _.isNumber(b.controls.left.x) ? b.controls.left.x : b.x; + b.controls.left.y = _.isNumber(b.controls.left.y) ? b.controls.left.y : b.y; + + b.controls.right.x = _.isNumber(b.controls.right.x) ? b.controls.right.x : b.x; + b.controls.right.y = _.isNumber(b.controls.right.y) ? b.controls.right.y : b.y; + + } + + }, + + /** + * Given three coordinates return the control points for the middle, b, + * vertex. + */ + getControlPoints: function(a, b, c) { + + var a1 = angleBetween(a, b); + var a2 = angleBetween(c, b); + + var d1 = distanceBetween(a, b); + var d2 = distanceBetween(c, b); + + var mid = (a1 + a2) / 2; + + // So we know which angle corresponds to which side. + + b.u = _.isObject(b.controls.left) ? b.controls.left : new Two.Vector(0, 0); + b.v = _.isObject(b.controls.right) ? b.controls.right : new Two.Vector(0, 0); + + // TODO: Issue 73 + if (d1 < 0.0001 || d2 < 0.0001) { + if (!b._relative) { + b.controls.left.copy(b); + b.controls.right.copy(b); + } + return b; + } + + d1 *= 0.33; // Why 0.33? + d2 *= 0.33; + + if (a2 < a1) { + mid += HALF_PI; + } else { + mid -= HALF_PI; + } + + b.controls.left.x = cos(mid) * d1; + b.controls.left.y = sin(mid) * d1; + + mid -= PI; + + b.controls.right.x = cos(mid) * d2; + b.controls.right.y = sin(mid) * d2; + + if (!b._relative) { + b.controls.left.x += b.x; + b.controls.left.y += b.y; + b.controls.right.x += b.x; + b.controls.right.y += b.y; + } + + return b; + + }, + + /** + * Get the reflection of a point "b" about point "a". Where "a" is in + * absolute space and "b" is relative to "a". + * + * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes + */ + getReflection: function(a, b, relative) { + + return new Two.Vector( + 2 * a.x - (b.x + a.x) - (relative ? a.x : 0), + 2 * a.y - (b.y + a.y) - (relative ? a.y : 0) + ); + + }, + + getAnchorsFromArcData: function(center, xAxisRotation, rx, ry, ts, td, ccw) { + + var matrix = new Two.Matrix() + .translate(center.x, center.y) + .rotate(xAxisRotation); + + var l = Two.Resolution; + + return _.map(_.range(l), function(i) { + + var pct = (i + 1) / l; + if (!!ccw) { + pct = 1 - pct; + } + + var theta = pct * td + ts; + var x = rx * Math.cos(theta); + var y = ry * Math.sin(theta); + + // x += center.x; + // y += center.y; + + var anchor = new Two.Anchor(x, y); + Two.Anchor.AppendCurveProperties(anchor); + anchor.command = Two.Commands.line; + + // TODO: Calculate control points here... + + return anchor; + + }); + + }, + + ratioBetween: function(A, B) { + + return (A.x * B.x + A.y * B.y) / (A.length() * B.length()); + + }, + + angleBetween: function(A, B) { + + var dx, dy; + + if (arguments.length >= 4) { + + dx = arguments[0] - arguments[2]; + dy = arguments[1] - arguments[3]; + + return atan2(dy, dx); + + } + + dx = A.x - B.x; + dy = A.y - B.y; + + return atan2(dy, dx); + + }, + + distanceBetweenSquared: function(p1, p2) { + + var dx = p1.x - p2.x; + var dy = p1.y - p2.y; + + return dx * dx + dy * dy; + + }, + + distanceBetween: function(p1, p2) { + + return sqrt(distanceBetweenSquared(p1, p2)); + + }, + + lerp: function(a, b, t) { + return t * (b - a) + a; + }, + + // A pretty fast toFixed(3) alternative + // See http://jsperf.com/parsefloat-tofixed-vs-math-round/18 + toFixed: function(v) { + return Math.floor(v * 1000) / 1000; + }, + + mod: function(v, l) { + + while (v < 0) { + v += l; + } + + return v % l; + + }, + + /** + * Array like collection that triggers inserted and removed events + * removed : pop / shift / splice + * inserted : push / unshift / splice (with > 2 arguments) + */ + Collection: function() { + + Array.call(this); + + if (arguments.length > 1) { + Array.prototype.push.apply(this, arguments); + } else if (arguments[0] && Array.isArray(arguments[0])) { + Array.prototype.push.apply(this, arguments[0]); + } + + }, + + // Custom Error Throwing for Two.js + + Error: function(message) { + this.name = 'two.js'; + this.message = message; + }, + + Events: { + + on: function(name, callback) { + + this._events || (this._events = {}); + var list = this._events[name] || (this._events[name] = []); + + list.push(callback); + + return this; + + }, + + off: function(name, callback) { + + if (!this._events) { + return this; + } + if (!name && !callback) { + this._events = {}; + return this; + } + + var names = name ? [name] : _.keys(this._events); + for (var i = 0, l = names.length; i < l; i++) { + + var name = names[i]; + var list = this._events[name]; + + if (!!list) { + var events = []; + if (callback) { + for (var j = 0, k = list.length; j < k; j++) { + var ev = list[j]; + ev = ev.callback ? ev.callback : ev; + if (callback && callback !== ev) { + events.push(ev); + } + } + } + this._events[name] = events; + } + } + + return this; + }, + + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + var events = this._events[name]; + if (events) trigger(this, events, args); + return this; + }, + + listen: function (obj, name, callback) { + + var bound = this; + + if (obj) { + var ev = function () { + callback.apply(bound, arguments); + }; + + // add references about the object that assigned this listener + ev.obj = obj; + ev.name = name; + ev.callback = callback; + + obj.on(name, ev); + } + + return this; + + }, + + ignore: function (obj, name, callback) { + + obj.off(name, callback); + + return this; + + } + + } + + }) + + }); + + Two.Utils.Events.bind = Two.Utils.Events.on; + Two.Utils.Events.unbind = Two.Utils.Events.off; + + var trigger = function(obj, events, args) { + var method; + switch (args.length) { + case 0: + method = function(i) { + events[i].call(obj, args[0]); + }; + break; + case 1: + method = function(i) { + events[i].call(obj, args[0], args[1]); + }; + break; + case 2: + method = function(i) { + events[i].call(obj, args[0], args[1], args[2]); + }; + break; + case 3: + method = function(i) { + events[i].call(obj, args[0], args[1], args[2], args[3]); + }; + break; + default: + method = function(i) { + events[i].apply(obj, args); + }; + } + for (var i = 0; i < events.length; i++) { + method(i); + } + }; + + Two.Utils.Error.prototype = new Error(); + Two.Utils.Error.prototype.constructor = Two.Utils.Error; + + Two.Utils.Collection.prototype = new Array(); + Two.Utils.Collection.prototype.constructor = Two.Utils.Collection; + + _.extend(Two.Utils.Collection.prototype, Two.Utils.Events, { + + pop: function() { + var popped = Array.prototype.pop.apply(this, arguments); + this.trigger(Two.Events.remove, [popped]); + return popped; + }, + + shift: function() { + var shifted = Array.prototype.shift.apply(this, arguments); + this.trigger(Two.Events.remove, [shifted]); + return shifted; + }, + + push: function() { + var pushed = Array.prototype.push.apply(this, arguments); + this.trigger(Two.Events.insert, arguments); + return pushed; + }, + + unshift: function() { + var unshifted = Array.prototype.unshift.apply(this, arguments); + this.trigger(Two.Events.insert, arguments); + return unshifted; + }, + + splice: function() { + var spliced = Array.prototype.splice.apply(this, arguments); + var inserted; + + this.trigger(Two.Events.remove, spliced); + + if (arguments.length > 2) { + inserted = this.slice(arguments[0], arguments[0] + arguments.length - 2); + this.trigger(Two.Events.insert, inserted); + this.trigger(Two.Events.order); + } + return spliced; + }, + + sort: function() { + Array.prototype.sort.apply(this, arguments); + this.trigger(Two.Events.order); + return this; + }, + + reverse: function() { + Array.prototype.reverse.apply(this, arguments); + this.trigger(Two.Events.order); + return this; + } + + }); + + // Localize utils + + var distanceBetween = Two.Utils.distanceBetween, + getAnchorsFromArcData = Two.Utils.getAnchorsFromArcData, + distanceBetweenSquared = Two.Utils.distanceBetweenSquared, + ratioBetween = Two.Utils.ratioBetween, + angleBetween = Two.Utils.angleBetween, + getControlPoints = Two.Utils.getControlPoints, + getCurveFromPoints = Two.Utils.getCurveFromPoints, + solveSegmentIntersection = Two.Utils.solveSegmentIntersection, + decoupleShapes = Two.Utils.decoupleShapes, + mod = Two.Utils.mod, + getBackingStoreRatio = Two.Utils.getBackingStoreRatio, + getPointOnCubicBezier = Two.Utils.getPointOnCubicBezier, + getCurveLength = Two.Utils.getCurveLength, + integrate = Two.Utils.integrate, + getReflection = Two.Utils.getReflection; + + _.extend(Two.prototype, Two.Utils.Events, { + + appendTo: function(elem) { + + elem.appendChild(this.renderer.domElement); + return this; + + }, + + play: function() { + + Two.Utils.setPlaying.call(this, true); + return this.trigger(Two.Events.play); + + }, + + pause: function() { + + this.playing = false; + return this.trigger(Two.Events.pause); + + }, + + /** + * Update positions and calculations in one pass before rendering. + */ + update: function() { + + var animated = !!this._lastFrame; + var now = perf.now(); + + this.frameCount++; + + if (animated) { + this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3)); + } + this._lastFrame = now; + + var width = this.width; + var height = this.height; + var renderer = this.renderer; + + // Update width / height for the renderer + if (width !== renderer.width || height !== renderer.height) { + renderer.setSize(width, height, this.ratio); + } + + this.trigger(Two.Events.update, this.frameCount, this.timeDelta); + + return this.render(); + + }, + + /** + * Render all drawable - visible objects of the scene. + */ + render: function() { + + this.renderer.render(); + return this.trigger(Two.Events.render, this.frameCount); + + }, + + /** + * Convenience Methods + */ + + add: function(o) { + + var objects = o; + if (!(objects instanceof Array)) { + objects = _.toArray(arguments); + } + + this.scene.add(objects); + return this; + + }, + + remove: function(o) { + + var objects = o; + if (!(objects instanceof Array)) { + objects = _.toArray(arguments); + } + + this.scene.remove(objects); + + return this; + + }, + + clear: function() { + + this.scene.remove(_.toArray(this.scene.children)); + return this; + + }, + + makeLine: function(x1, y1, x2, y2) { + + var line = new Two.Line(x1, y1, x2, y2); + this.scene.add(line); + + return line; + + }, + + makeRectangle: function(x, y, width, height) { + + var rect = new Two.Rectangle(x, y, width, height); + this.scene.add(rect); + + return rect; + + }, + + makeRoundedRectangle: function(x, y, width, height, sides) { + + var rect = new Two.RoundedRectangle(x, y, width, height, sides); + this.scene.add(rect); + + return rect; + + }, + + makeCircle: function(ox, oy, r) { + + var circle = new Two.Circle(ox, oy, r); + this.scene.add(circle); + + return circle; + + }, + + makeEllipse: function(ox, oy, rx, ry) { + + var ellipse = new Two.Ellipse(ox, oy, rx, ry); + this.scene.add(ellipse); + + return ellipse; + + }, + + makeStar: function(ox, oy, or, ir, sides) { + + var star = new Two.Star(ox, oy, or, ir, sides); + this.scene.add(star); + + return star; + + }, + + makeCurve: function(p) { + + var l = arguments.length, points = p; + if (!_.isArray(p)) { + points = []; + for (var i = 0; i < l; i+=2) { + var x = arguments[i]; + if (!_.isNumber(x)) { + break; + } + var y = arguments[i + 1]; + points.push(new Two.Anchor(x, y)); + } + } + + var last = arguments[l - 1]; + var curve = new Two.Path(points, !(_.isBoolean(last) ? last : undefined), true); + var rect = curve.getBoundingClientRect(); + curve.center().translation + .set(rect.left + rect.width / 2, rect.top + rect.height / 2); + + this.scene.add(curve); + + return curve; + + }, + + makePolygon: function(ox, oy, r, sides) { + + var poly = new Two.Polygon(ox, oy, r, sides); + this.scene.add(poly); + + return poly; + + }, + + /* + * Make an Arc Segment + */ + + makeArcSegment: function(ox, oy, ir, or, sa, ea, res) { + var arcSegment = new Two.ArcSegment(ox, oy, ir, or, sa, ea, res); + this.scene.add(arcSegment); + return arcSegment; + }, + + /** + * Convenience method to make and draw a Two.Path. + */ + makePath: function(p) { + + var l = arguments.length, points = p; + if (!_.isArray(p)) { + points = []; + for (var i = 0; i < l; i+=2) { + var x = arguments[i]; + if (!_.isNumber(x)) { + break; + } + var y = arguments[i + 1]; + points.push(new Two.Anchor(x, y)); + } + } + + var last = arguments[l - 1]; + var path = new Two.Path(points, !(_.isBoolean(last) ? last : undefined)); + var rect = path.getBoundingClientRect(); + path.center().translation + .set(rect.left + rect.width / 2, rect.top + rect.height / 2); + + this.scene.add(path); + + return path; + + }, + + /** + * Convenience method to make and add a Two.Text. + */ + makeText: function(message, x, y, styles) { + var text = new Two.Text(message, x, y, styles); + this.add(text); + return text; + }, + + /** + * Convenience method to make and add a Two.LinearGradient. + */ + makeLinearGradient: function(x1, y1, x2, y2 /* stops */) { + + var stops = slice.call(arguments, 4); + var gradient = new Two.LinearGradient(x1, y1, x2, y2, stops); + + this.add(gradient); + + return gradient; + + }, + + /** + * Convenience method to make and add a Two.RadialGradient. + */ + makeRadialGradient: function(x1, y1, r /* stops */) { + + var stops = slice.call(arguments, 3); + var gradient = new Two.RadialGradient(x1, y1, r, stops); + + this.add(gradient); + + return gradient; + + }, + + makeSprite: function(path, x, y, cols, rows, frameRate, autostart) { + + var sprite = new Two.Sprite(path, x, y, cols, rows, frameRate); + if (!!autostart) { + sprite.play(); + } + this.add(sprite); + + return sprite; + + }, + + makeImageSequence: function(paths, x, y, frameRate, autostart) { + + var imageSequence = new Two.ImageSequence(paths, x, y, frameRate); + if (!!autostart) { + imageSequence.play(); + } + this.add(imageSequence); + + return imageSequence; + + }, + + makeTexture: function(path, callback) { + + var texture = new Two.Texture(path, callback); + return texture; + + }, + + makeGroup: function(o) { + + var objects = o; + if (!(objects instanceof Array)) { + objects = _.toArray(arguments); + } + + var group = new Two.Group(); + this.scene.add(group); + group.add(objects); + + return group; + + }, + + /** + * Interpret an SVG Node and add it to this instance's scene. The + * distinction should be made that this doesn't `import` svg's, it solely + * interprets them into something compatible for Two.js — this is slightly + * different than a direct transcription. + * + * @param {Object} svgNode - The node to be parsed + * @param {Boolean} shallow - Don't create a top-most group but + * append all contents directly + */ + interpret: function(svgNode, shallow) { + + var tag = svgNode.tagName.toLowerCase(); + + if (!(tag in Two.Utils.read)) { + return null; + } + + var node = Two.Utils.read[tag].call(this, svgNode); + + if (shallow && node instanceof Two.Group) { + this.add(node.children); + } else { + this.add(node); + } + + return node; + + }, + + /** + * Load an SVG file / text and interpret. + */ + load: function(text, callback) { + + var nodes = [], elem, i; + + if (/.*\.svg/ig.test(text)) { + + Two.Utils.xhr(text, _.bind(function(data) { + + dom.temp.innerHTML = data; + for (i = 0; i < dom.temp.children.length; i++) { + elem = dom.temp.children[i]; + nodes.push(this.interpret(elem)); + } + + callback(nodes.length <= 1 ? nodes[0] : nodes, + dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children); + + }, this)); + + return this; + + } + + dom.temp.innerHTML = text; + for (i = 0; i < dom.temp.children.length; i++) { + elem = dom.temp.children[i]; + nodes.push(this.interpret(elem)); + } + + callback(nodes.length <= 1 ? nodes[0] : nodes, + dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children); + + return this; + + } + + }); + + function fitToWindow() { + + var wr = document.body.getBoundingClientRect(); + + var width = this.width = wr.width; + var height = this.height = wr.height; + + this.renderer.setSize(width, height, this.ratio); + this.trigger(Two.Events.resize, width, height); + + } + + // Request Animation Frame + + var raf = dom.getRequestAnimationFrame(); + + function loop() { + + raf(loop); + + for (var i = 0; i < Two.Instances.length; i++) { + var t = Two.Instances[i]; + if (t.playing) { + t.update(); + } + } + + } + + if (typeof define === 'function' && define.amd) { + define('two', [], function() { + return Two; + }); + } else if (typeof module != 'undefined' && module.exports) { + module.exports = Two; + } + + return Two; + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var Registry = Two.Registry = function() { + + this.map = {}; + + }; + + _.extend(Registry, { + + }); + + _.extend(Registry.prototype, { + + add: function(id, obj) { + this.map[id] = obj; + return this; + }, + + remove: function(id) { + delete this.map[id]; + return this; + }, + + get: function(id) { + return this.map[id]; + }, + + contains: function(id) { + return id in this.map; + } + + }); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var Vector = Two.Vector = function(x, y) { + + this.x = x || 0; + this.y = y || 0; + + }; + + _.extend(Vector, { + + zero: new Two.Vector() + + }); + + _.extend(Vector.prototype, Two.Utils.Events, { + + set: function(x, y) { + this.x = x; + this.y = y; + return this; + }, + + copy: function(v) { + this.x = v.x; + this.y = v.y; + return this; + }, + + clear: function() { + this.x = 0; + this.y = 0; + return this; + }, + + clone: function() { + return new Vector(this.x, this.y); + }, + + add: function(v1, v2) { + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + return this; + }, + + addSelf: function(v) { + this.x += v.x; + this.y += v.y; + return this; + }, + + sub: function(v1, v2) { + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + return this; + }, + + subSelf: function(v) { + this.x -= v.x; + this.y -= v.y; + return this; + }, + + multiplySelf: function(v) { + this.x *= v.x; + this.y *= v.y; + return this; + }, + + multiplyScalar: function(s) { + this.x *= s; + this.y *= s; + return this; + }, + + divideScalar: function(s) { + if (s) { + this.x /= s; + this.y /= s; + } else { + this.set(0, 0); + } + return this; + }, + + negate: function() { + return this.multiplyScalar(-1); + }, + + dot: function(v) { + return this.x * v.x + this.y * v.y; + }, + + lengthSquared: function() { + return this.x * this.x + this.y * this.y; + }, + + length: function() { + return Math.sqrt(this.lengthSquared()); + }, + + normalize: function() { + return this.divideScalar(this.length()); + }, + + distanceTo: function(v) { + return Math.sqrt(this.distanceToSquared(v)); + }, + + distanceToSquared: function(v) { + var dx = this.x - v.x, + dy = this.y - v.y; + return dx * dx + dy * dy; + }, + + setLength: function(l) { + return this.normalize().multiplyScalar(l); + }, + + equals: function(v, eps) { + eps = (typeof eps === 'undefined') ? 0.0001 : eps; + return (this.distanceTo(v) < eps); + }, + + lerp: function(v, t) { + var x = (v.x - this.x) * t + this.x; + var y = (v.y - this.y) * t + this.y; + return this.set(x, y); + }, + + isZero: function(eps) { + eps = (typeof eps === 'undefined') ? 0.0001 : eps; + return (this.length() < eps); + }, + + toString: function() { + return this.x + ', ' + this.y; + }, + + toObject: function() { + return { x: this.x, y: this.y }; + }, + + rotate: function (radians) { + var cos = Math.cos(radians); + var sin = Math.sin(radians); + this.x = this.x * cos - this.y * sin; + this.y = this.x * sin + this.y * cos; + return this; + } + + }); + + var BoundProto = { + + set: function(x, y) { + this._x = x; + this._y = y; + return this.trigger(Two.Events.change); + }, + + copy: function(v) { + this._x = v.x; + this._y = v.y; + return this.trigger(Two.Events.change); + }, + + clear: function() { + this._x = 0; + this._y = 0; + return this.trigger(Two.Events.change); + }, + + clone: function() { + return new Vector(this._x, this._y); + }, + + add: function(v1, v2) { + this._x = v1.x + v2.x; + this._y = v1.y + v2.y; + return this.trigger(Two.Events.change); + }, + + addSelf: function(v) { + this._x += v.x; + this._y += v.y; + return this.trigger(Two.Events.change); + }, + + sub: function(v1, v2) { + this._x = v1.x - v2.x; + this._y = v1.y - v2.y; + return this.trigger(Two.Events.change); + }, + + subSelf: function(v) { + this._x -= v.x; + this._y -= v.y; + return this.trigger(Two.Events.change); + }, + + multiplySelf: function(v) { + this._x *= v.x; + this._y *= v.y; + return this.trigger(Two.Events.change); + }, + + multiplyScalar: function(s) { + this._x *= s; + this._y *= s; + return this.trigger(Two.Events.change); + }, + + divideScalar: function(s) { + if (s) { + this._x /= s; + this._y /= s; + return this.trigger(Two.Events.change); + } + return this.clear(); + }, + + negate: function() { + return this.multiplyScalar(-1); + }, + + dot: function(v) { + return this._x * v.x + this._y * v.y; + }, + + lengthSquared: function() { + return this._x * this._x + this._y * this._y; + }, + + length: function() { + return Math.sqrt(this.lengthSquared()); + }, + + normalize: function() { + return this.divideScalar(this.length()); + }, + + distanceTo: function(v) { + return Math.sqrt(this.distanceToSquared(v)); + }, + + distanceToSquared: function(v) { + var dx = this._x - v.x, + dy = this._y - v.y; + return dx * dx + dy * dy; + }, + + setLength: function(l) { + return this.normalize().multiplyScalar(l); + }, + + equals: function(v, eps) { + eps = (typeof eps === 'undefined') ? 0.0001 : eps; + return (this.distanceTo(v) < eps); + }, + + lerp: function(v, t) { + var x = (v.x - this._x) * t + this._x; + var y = (v.y - this._y) * t + this._y; + return this.set(x, y); + }, + + isZero: function(eps) { + eps = (typeof eps === 'undefined') ? 0.0001 : eps; + return (this.length() < eps); + }, + + toString: function() { + return this._x + ', ' + this._y; + }, + + toObject: function() { + return { x: this._x, y: this._y }; + }, + + rotate: function (radians) { + var cos = Math.cos(radians); + var sin = Math.sin(radians); + this._x = this._x * cos - this._y * sin; + this._y = this._x * sin + this._y * cos; + return this; + } + + }; + + var xgs = { + enumerable: true, + get: function() { + return this._x; + }, + set: function(v) { + this._x = v; + this.trigger(Two.Events.change, 'x'); + } + }; + + var ygs = { + enumerable: true, + get: function() { + return this._y; + }, + set: function(v) { + this._y = v; + this.trigger(Two.Events.change, 'y'); + } + }; + + /** + * Override Backbone bind / on in order to add properly broadcasting. + * This allows Two.Vector to not broadcast events unless event listeners + * are explicity bound to it. + */ + + Two.Vector.prototype.bind = Two.Vector.prototype.on = function() { + + if (!this._bound) { + this._x = this.x; + this._y = this.y; + Object.defineProperty(this, 'x', xgs); + Object.defineProperty(this, 'y', ygs); + _.extend(this, BoundProto); + this._bound = true; // Reserved for event initialization check + } + + Two.Utils.Events.bind.apply(this, arguments); + + return this; + + }; + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + // Localized variables + var commands = Two.Commands; + var _ = Two.Utils; + + /** + * An object that holds 3 `Two.Vector`s, the anchor point and its + * corresponding handles: `left` and `right`. + */ + var Anchor = Two.Anchor = function(x, y, ux, uy, vx, vy, command) { + + Two.Vector.call(this, x, y); + + this._broadcast = _.bind(function() { + this.trigger(Two.Events.change); + }, this); + + this._command = command || commands.move; + this._relative = true; + + if (!command) { + return this; + } + + Anchor.AppendCurveProperties(this); + + if (_.isNumber(ux)) { + this.controls.left.x = ux; + } + if (_.isNumber(uy)) { + this.controls.left.y = uy; + } + if (_.isNumber(vx)) { + this.controls.right.x = vx; + } + if (_.isNumber(vy)) { + this.controls.right.y = vy; + } + + }; + + _.extend(Anchor, { + + AppendCurveProperties: function(anchor) { + anchor.controls = { + left: new Two.Vector(0, 0), + right: new Two.Vector(0, 0) + }; + } + + }); + + var AnchorProto = { + + listen: function() { + + if (!_.isObject(this.controls)) { + Anchor.AppendCurveProperties(this); + } + + this.controls.left.bind(Two.Events.change, this._broadcast); + this.controls.right.bind(Two.Events.change, this._broadcast); + + return this; + + }, + + ignore: function() { + + this.controls.left.unbind(Two.Events.change, this._broadcast); + this.controls.right.unbind(Two.Events.change, this._broadcast); + + return this; + + }, + + clone: function() { + + var controls = this.controls; + + var clone = new Two.Anchor( + this.x, + this.y, + controls && controls.left.x, + controls && controls.left.y, + controls && controls.right.x, + controls && controls.right.y, + this.command + ); + clone.relative = this._relative; + return clone; + + }, + + toObject: function() { + var o = { + x: this.x, + y: this.y + }; + if (this._command) { + o.command = this._command; + } + if (this._relative) { + o.relative = this._relative; + } + if (this.controls) { + o.controls = { + left: this.controls.left.toObject(), + right: this.controls.right.toObject() + }; + } + return o; + }, + + toString: function() { + if (!this.controls) { + return [this._x, this._y].join(', '); + } + return [this._x, this._y, this.controls.left.x, this.controls.left.y, + this.controls.right.x, this.controls.right.y].join(', '); + } + + }; + + Object.defineProperty(Anchor.prototype, 'command', { + + enumerable: true, + + get: function() { + return this._command; + }, + + set: function(c) { + this._command = c; + if (this._command === commands.curve && !_.isObject(this.controls)) { + Anchor.AppendCurveProperties(this); + } + return this.trigger(Two.Events.change); + } + + }); + + Object.defineProperty(Anchor.prototype, 'relative', { + + enumerable: true, + + get: function() { + return this._relative; + }, + + set: function(b) { + if (this._relative == b) { + return this; + } + this._relative = !!b; + return this.trigger(Two.Events.change); + } + + }); + + _.extend(Anchor.prototype, Two.Vector.prototype, AnchorProto); + + // Make it possible to bind and still have the Anchor specific + // inheritance from Two.Vector + Two.Anchor.prototype.bind = Two.Anchor.prototype.on = function() { + Two.Vector.prototype.bind.apply(this, arguments); + _.extend(this, AnchorProto); + }; + + Two.Anchor.prototype.unbind = Two.Anchor.prototype.off = function() { + Two.Vector.prototype.unbind.apply(this, arguments); + _.extend(this, AnchorProto); + }; + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + /** + * Constants + */ + var cos = Math.cos, sin = Math.sin, tan = Math.tan; + var _ = Two.Utils; + + /** + * Two.Matrix contains an array of elements that represent + * the two dimensional 3 x 3 matrix as illustrated below: + * + * ===== + * a b c + * d e f + * g h i // this row is not really used in 2d transformations + * ===== + * + * String order is for transform strings: a, d, b, e, c, f + * + * @class + */ + var Matrix = Two.Matrix = function(a, b, c, d, e, f) { + + this.elements = new Two.Array(9); + + var elements = a; + if (!_.isArray(elements)) { + elements = _.toArray(arguments); + } + + // initialize the elements with default values. + + this.identity().set(elements); + + }; + + _.extend(Matrix, { + + Identity: [ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ], + + /** + * Multiply two matrix 3x3 arrays + */ + Multiply: function(A, B, C) { + + if (B.length <= 3) { // Multiply Vector + + var x, y, z, e = A; + + var a = B[0] || 0, + b = B[1] || 0, + c = B[2] || 0; + + // Go down rows first + // a, d, g, b, e, h, c, f, i + + x = e[0] * a + e[1] * b + e[2] * c; + y = e[3] * a + e[4] * b + e[5] * c; + z = e[6] * a + e[7] * b + e[8] * c; + + return { x: x, y: y, z: z }; + + } + + var A0 = A[0], A1 = A[1], A2 = A[2]; + var A3 = A[3], A4 = A[4], A5 = A[5]; + var A6 = A[6], A7 = A[7], A8 = A[8]; + + var B0 = B[0], B1 = B[1], B2 = B[2]; + var B3 = B[3], B4 = B[4], B5 = B[5]; + var B6 = B[6], B7 = B[7], B8 = B[8]; + + C = C || new Two.Array(9); + + C[0] = A0 * B0 + A1 * B3 + A2 * B6; + C[1] = A0 * B1 + A1 * B4 + A2 * B7; + C[2] = A0 * B2 + A1 * B5 + A2 * B8; + C[3] = A3 * B0 + A4 * B3 + A5 * B6; + C[4] = A3 * B1 + A4 * B4 + A5 * B7; + C[5] = A3 * B2 + A4 * B5 + A5 * B8; + C[6] = A6 * B0 + A7 * B3 + A8 * B6; + C[7] = A6 * B1 + A7 * B4 + A8 * B7; + C[8] = A6 * B2 + A7 * B5 + A8 * B8; + + return C; + + } + + }); + + _.extend(Matrix.prototype, Two.Utils.Events, { + + /** + * Takes an array of elements or the arguments list itself to + * set and update the current matrix's elements. Only updates + * specified values. + */ + set: function(a) { + + var elements = a; + if (!_.isArray(elements)) { + elements = _.toArray(arguments); + } + + _.extend(this.elements, elements); + + return this.trigger(Two.Events.change); + + }, + + /** + * Turn matrix to identity, like resetting. + */ + identity: function() { + + this.set(Matrix.Identity); + + return this; + + }, + + /** + * Multiply scalar or multiply by another matrix. + */ + multiply: function(a, b, c, d, e, f, g, h, i) { + + var elements = arguments, l = elements.length; + + // Multiply scalar + + if (l <= 1) { + + _.each(this.elements, function(v, i) { + this.elements[i] = v * a; + }, this); + + return this.trigger(Two.Events.change); + + } + + if (l <= 3) { // Multiply Vector + + var x, y, z; + a = a || 0; + b = b || 0; + c = c || 0; + e = this.elements; + + // Go down rows first + // a, d, g, b, e, h, c, f, i + + x = e[0] * a + e[1] * b + e[2] * c; + y = e[3] * a + e[4] * b + e[5] * c; + z = e[6] * a + e[7] * b + e[8] * c; + + return { x: x, y: y, z: z }; + + } + + // Multiple matrix + + var A = this.elements; + var B = elements; + + var A0 = A[0], A1 = A[1], A2 = A[2]; + var A3 = A[3], A4 = A[4], A5 = A[5]; + var A6 = A[6], A7 = A[7], A8 = A[8]; + + var B0 = B[0], B1 = B[1], B2 = B[2]; + var B3 = B[3], B4 = B[4], B5 = B[5]; + var B6 = B[6], B7 = B[7], B8 = B[8]; + + this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6; + this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7; + this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8; + + this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6; + this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7; + this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8; + + this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6; + this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7; + this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8; + + return this.trigger(Two.Events.change); + + }, + + inverse: function(out) { + + var a = this.elements; + out = out || new Two.Matrix(); + + var a00 = a[0], a01 = a[1], a02 = a[2]; + var a10 = a[3], a11 = a[4], a12 = a[5]; + var a20 = a[6], a21 = a[7], a22 = a[8]; + + var b01 = a22 * a11 - a12 * a21; + var b11 = -a22 * a10 + a12 * a20; + var b21 = a21 * a10 - a11 * a20; + + // Calculate the determinant + var det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + + det = 1.0 / det; + + out.elements[0] = b01 * det; + out.elements[1] = (-a22 * a01 + a02 * a21) * det; + out.elements[2] = (a12 * a01 - a02 * a11) * det; + out.elements[3] = b11 * det; + out.elements[4] = (a22 * a00 - a02 * a20) * det; + out.elements[5] = (-a12 * a00 + a02 * a10) * det; + out.elements[6] = b21 * det; + out.elements[7] = (-a21 * a00 + a01 * a20) * det; + out.elements[8] = (a11 * a00 - a01 * a10) * det; + + return out; + + }, + + /** + * Set a scalar onto the matrix. + */ + scale: function(sx, sy) { + + var l = arguments.length; + if (l <= 1) { + sy = sx; + } + + return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1); + + }, + + /** + * Rotate the matrix. + */ + rotate: function(radians) { + + var c = cos(radians); + var s = sin(radians); + + return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1); + + }, + + /** + * Translate the matrix. + */ + translate: function(x, y) { + + return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1); + + }, + + /* + * Skew the matrix by an angle in the x axis direction. + */ + skewX: function(radians) { + + var a = tan(radians); + + return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1); + + }, + + /* + * Skew the matrix by an angle in the y axis direction. + */ + skewY: function(radians) { + + var a = tan(radians); + + return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1); + + }, + + /** + * Create a transform string to be used with rendering apis. + */ + toString: function(fullMatrix) { + var temp = []; + + this.toArray(fullMatrix, temp); + + return temp.join(' '); + + }, + + /** + * Create a transform array to be used with rendering apis. + */ + toArray: function(fullMatrix, output) { + + var elements = this.elements; + var hasOutput = !!output; + + var a = parseFloat(elements[0].toFixed(3)); + var b = parseFloat(elements[1].toFixed(3)); + var c = parseFloat(elements[2].toFixed(3)); + var d = parseFloat(elements[3].toFixed(3)); + var e = parseFloat(elements[4].toFixed(3)); + var f = parseFloat(elements[5].toFixed(3)); + + if (!!fullMatrix) { + + var g = parseFloat(elements[6].toFixed(3)); + var h = parseFloat(elements[7].toFixed(3)); + var i = parseFloat(elements[8].toFixed(3)); + + if (hasOutput) { + output[0] = a; + output[1] = d; + output[2] = g; + output[3] = b; + output[4] = e; + output[5] = h; + output[6] = c; + output[7] = f; + output[8] = i; + return; + } + + return [ + a, d, g, b, e, h, c, f, i + ]; + } + + if (hasOutput) { + output[0] = a; + output[1] = d; + output[2] = b; + output[3] = e; + output[4] = c; + output[5] = f; + return; + } + + return [ + a, d, b, e, c, f // Specific format see LN:19 + ]; + + }, + + /** + * Clone the current matrix. + */ + clone: function() { + var a, b, c, d, e, f, g, h, i; + + a = this.elements[0]; + b = this.elements[1]; + c = this.elements[2]; + d = this.elements[3]; + e = this.elements[4]; + f = this.elements[5]; + g = this.elements[6]; + h = this.elements[7]; + i = this.elements[8]; + + return new Two.Matrix(a, b, c, d, e, f, g, h, i); + + } + + }); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + // Localize variables + var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed; + var _ = Two.Utils; + + var svg = { + + version: 1.1, + + ns: 'http://www.w3.org/2000/svg', + xlink: 'http://www.w3.org/1999/xlink', + + alignments: { + left: 'start', + center: 'middle', + right: 'end' + }, + + /** + * Create an svg namespaced element. + */ + createElement: function(name, attrs) { + var tag = name; + var elem = document.createElementNS(svg.ns, tag); + if (tag === 'svg') { + attrs = _.defaults(attrs || {}, { + version: svg.version + }); + } + if (!_.isEmpty(attrs)) { + svg.setAttributes(elem, attrs); + } + return elem; + }, + + /** + * Add attributes from an svg element. + */ + setAttributes: function(elem, attrs) { + var keys = Object.keys(attrs); + for (var i = 0; i < keys.length; i++) { + if (/href/.test(keys[i])) { + elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]); + } else { + elem.setAttribute(keys[i], attrs[keys[i]]); + } + } + return this; + }, + + /** + * Remove attributes from an svg element. + */ + removeAttributes: function(elem, attrs) { + for (var key in attrs) { + elem.removeAttribute(key); + } + return this; + }, + + /** + * Turn a set of vertices into a string for the d property of a path + * element. It is imperative that the string collation is as fast as + * possible, because this call will be happening multiple times a + * second. + */ + toString: function(points, closed) { + + var l = points.length, + last = l - 1, + d, // The elusive last Two.Commands.move point + ret = ''; + + for (var i = 0; i < l; i++) { + var b = points[i]; + var command; + var prev = closed ? mod(i - 1, l) : Math.max(i - 1, 0); + var next = closed ? mod(i + 1, l) : Math.min(i + 1, last); + + var a = points[prev]; + var c = points[next]; + + var vx, vy, ux, uy, ar, bl, br, cl; + + // Access x and y directly, + // bypassing the getter + var x = toFixed(b._x); + var y = toFixed(b._y); + + switch (b._command) { + + case Two.Commands.close: + command = Two.Commands.close; + break; + + case Two.Commands.curve: + + ar = (a.controls && a.controls.right) || Two.Vector.zero; + bl = (b.controls && b.controls.left) || Two.Vector.zero; + + if (a._relative) { + vx = toFixed((ar.x + a.x)); + vy = toFixed((ar.y + a.y)); + } else { + vx = toFixed(ar.x); + vy = toFixed(ar.y); + } + + if (b._relative) { + ux = toFixed((bl.x + b.x)); + uy = toFixed((bl.y + b.y)); + } else { + ux = toFixed(bl.x); + uy = toFixed(bl.y); + } + + command = ((i === 0) ? Two.Commands.move : Two.Commands.curve) + + ' ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y; + break; + + case Two.Commands.move: + d = b; + command = Two.Commands.move + ' ' + x + ' ' + y; + break; + + default: + command = b._command + ' ' + x + ' ' + y; + + } + + // Add a final point and close it off + + if (i >= last && closed) { + + if (b._command === Two.Commands.curve) { + + // Make sure we close to the most previous Two.Commands.move + c = d; + + br = (b.controls && b.controls.right) || b; + cl = (c.controls && c.controls.left) || c; + + if (b._relative) { + vx = toFixed((br.x + b.x)); + vy = toFixed((br.y + b.y)); + } else { + vx = toFixed(br.x); + vy = toFixed(br.y); + } + + if (c._relative) { + ux = toFixed((cl.x + c.x)); + uy = toFixed((cl.y + c.y)); + } else { + ux = toFixed(cl.x); + uy = toFixed(cl.y); + } + + x = toFixed(c.x); + y = toFixed(c.y); + + command += + ' C ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y; + } + + command += ' Z'; + + } + + ret += command + ' '; + + } + + return ret; + + }, + + getClip: function(shape) { + + var clip = shape._renderer.clip; + + if (!clip) { + + var root = shape; + + while (root.parent) { + root = root.parent; + } + + clip = shape._renderer.clip = svg.createElement('clipPath'); + root.defs.appendChild(clip); + + } + + return clip; + + }, + + group: { + + // TODO: Can speed up. + // TODO: How does this effect a f + appendChild: function(object) { + + var elem = object._renderer.elem; + + if (!elem) { + return; + } + + var tag = elem.nodeName; + + if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) { + return; + } + + this.elem.appendChild(elem); + + }, + + removeChild: function(object) { + + var elem = object._renderer.elem; + + if (!elem || elem.parentNode != this.elem) { + return; + } + + var tag = elem.nodeName; + + if (!tag) { + return; + } + + // Defer subtractions while clipping. + if (object._clip) { + return; + } + + this.elem.removeChild(elem); + + }, + + orderChild: function(object) { + this.elem.appendChild(object._renderer.elem); + }, + + renderChild: function(child) { + svg[child._renderer.type].render.call(child, this); + }, + + render: function(domElement) { + + this._update(); + + // Shortcut for hidden objects. + // Doesn't reset the flags, so changes are stored and + // applied once the object is visible again + if (this._opacity === 0 && !this._flagOpacity) { + return this; + } + + if (!this._renderer.elem) { + this._renderer.elem = svg.createElement('g', { + id: this.id + }); + domElement.appendChild(this._renderer.elem); + } + + // _Update styles for the + var flagMatrix = this._matrix.manual || this._flagMatrix; + var context = { + domElement: domElement, + elem: this._renderer.elem + }; + + if (flagMatrix) { + this._renderer.elem.setAttribute('transform', 'matrix(' + this._matrix.toString() + ')'); + } + + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + svg[child._renderer.type].render.call(child, domElement); + } + + if (this._flagOpacity) { + this._renderer.elem.setAttribute('opacity', this._opacity); + } + + if (this._flagAdditions) { + this.additions.forEach(svg.group.appendChild, context); + } + + if (this._flagSubtractions) { + this.subtractions.forEach(svg.group.removeChild, context); + } + + if (this._flagOrder) { + this.children.forEach(svg.group.orderChild, context); + } + + /** + * Commented two-way functionality of clips / masks with groups and + * polygons. Uncomment when this bug is fixed: + * https://code.google.com/p/chromium/issues/detail?id=370951 + */ + + // if (this._flagClip) { + + // clip = svg.getClip(this); + // elem = this._renderer.elem; + + // if (this._clip) { + // elem.removeAttribute('id'); + // clip.setAttribute('id', this.id); + // clip.appendChild(elem); + // } else { + // clip.removeAttribute('id'); + // elem.setAttribute('id', this.id); + // this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore + // } + + // } + + if (this._flagMask) { + if (this._mask) { + this._renderer.elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')'); + } else { + this._renderer.elem.removeAttribute('clip-path'); + } + } + + return this.flagReset(); + + } + + }, + + path: { + + render: function(domElement) { + + this._update(); + + // Shortcut for hidden objects. + // Doesn't reset the flags, so changes are stored and + // applied once the object is visible again + if (this._opacity === 0 && !this._flagOpacity) { + return this; + } + + // Collect any attribute that needs to be changed here + var changed = {}; + + var flagMatrix = this._matrix.manual || this._flagMatrix; + + if (flagMatrix) { + changed.transform = 'matrix(' + this._matrix.toString() + ')'; + } + + if (this._flagVertices) { + var vertices = svg.toString(this._vertices, this._closed); + changed.d = vertices; + } + + if (this._fill && this._fill._renderer) { + this._fill._update(); + svg[this._fill._renderer.type].render.call(this._fill, domElement, true); + } + + if (this._flagFill) { + changed.fill = this._fill && this._fill.id + ? 'url(#' + this._fill.id + ')' : this._fill; + } + + if (this._stroke && this._stroke._renderer) { + this._stroke._update(); + svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true); + } + + if (this._flagStroke) { + changed.stroke = this._stroke && this._stroke.id + ? 'url(#' + this._stroke.id + ')' : this._stroke; + } + + if (this._flagLinewidth) { + changed['stroke-width'] = this._linewidth; + } + + if (this._flagOpacity) { + changed['stroke-opacity'] = this._opacity; + changed['fill-opacity'] = this._opacity; + } + + if (this._flagVisible) { + changed.visibility = this._visible ? 'visible' : 'hidden'; + } + + if (this._flagCap) { + changed['stroke-linecap'] = this._cap; + } + + if (this._flagJoin) { + changed['stroke-linejoin'] = this._join; + } + + if (this._flagMiter) { + changed['stroke-miterlimit'] = this._miter; + } + + // If there is no attached DOM element yet, + // create it with all necessary attributes. + if (!this._renderer.elem) { + + changed.id = this.id; + this._renderer.elem = svg.createElement('path', changed); + domElement.appendChild(this._renderer.elem); + + // Otherwise apply all pending attributes + } else { + svg.setAttributes(this._renderer.elem, changed); + } + + if (this._flagClip) { + + var clip = svg.getClip(this); + var elem = this._renderer.elem; + + if (this._clip) { + elem.removeAttribute('id'); + clip.setAttribute('id', this.id); + clip.appendChild(elem); + } else { + clip.removeAttribute('id'); + elem.setAttribute('id', this.id); + this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore + } + + } + + /** + * Commented two-way functionality of clips / masks with groups and + * polygons. Uncomment when this bug is fixed: + * https://code.google.com/p/chromium/issues/detail?id=370951 + */ + + // if (this._flagMask) { + // if (this._mask) { + // elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')'); + // } else { + // elem.removeAttribute('clip-path'); + // } + // } + + return this.flagReset(); + + } + + }, + + text: { + + render: function(domElement) { + + this._update(); + + var changed = {}; + + var flagMatrix = this._matrix.manual || this._flagMatrix; + + if (flagMatrix) { + changed.transform = 'matrix(' + this._matrix.toString() + ')'; + } + + if (this._flagFamily) { + changed['font-family'] = this._family; + } + if (this._flagSize) { + changed['font-size'] = this._size; + } + if (this._flagLeading) { + changed['line-height'] = this._leading; + } + if (this._flagAlignment) { + changed['text-anchor'] = svg.alignments[this._alignment] || this._alignment; + } + if (this._flagBaseline) { + changed['alignment-baseline'] = changed['dominant-baseline'] = this._baseline; + } + if (this._flagStyle) { + changed['font-style'] = this._style; + } + if (this._flagWeight) { + changed['font-weight'] = this._weight; + } + if (this._flagDecoration) { + changed['text-decoration'] = this._decoration; + } + if (this._fill && this._fill._renderer) { + this._fill._update(); + svg[this._fill._renderer.type].render.call(this._fill, domElement, true); + } + if (this._flagFill) { + changed.fill = this._fill && this._fill.id + ? 'url(#' + this._fill.id + ')' : this._fill; + } + if (this._stroke && this._stroke._renderer) { + this._stroke._update(); + svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true); + } + if (this._flagStroke) { + changed.stroke = this._stroke && this._stroke.id + ? 'url(#' + this._stroke.id + ')' : this._stroke; + } + if (this._flagLinewidth) { + changed['stroke-width'] = this._linewidth; + } + if (this._flagOpacity) { + changed.opacity = this._opacity; + } + if (this._flagVisible) { + changed.visibility = this._visible ? 'visible' : 'hidden'; + } + + if (!this._renderer.elem) { + + changed.id = this.id; + + this._renderer.elem = svg.createElement('text', changed); + domElement.defs.appendChild(this._renderer.elem); + + } else { + + svg.setAttributes(this._renderer.elem, changed); + + } + + if (this._flagClip) { + + var clip = svg.getClip(this); + var elem = this._renderer.elem; + + if (this._clip) { + elem.removeAttribute('id'); + clip.setAttribute('id', this.id); + clip.appendChild(elem); + } else { + clip.removeAttribute('id'); + elem.setAttribute('id', this.id); + this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore + } + + } + + if (this._flagValue) { + this._renderer.elem.textContent = this._value; + } + + return this.flagReset(); + + } + + }, + + 'linear-gradient': { + + render: function(domElement, silent) { + + if (!silent) { + this._update(); + } + + var changed = {}; + + if (this._flagEndPoints) { + changed.x1 = this.left._x; + changed.y1 = this.left._y; + changed.x2 = this.right._x; + changed.y2 = this.right._y; + } + + if (this._flagSpread) { + changed.spreadMethod = this._spread; + } + + // If there is no attached DOM element yet, + // create it with all necessary attributes. + if (!this._renderer.elem) { + + changed.id = this.id; + changed.gradientUnits = 'userSpaceOnUse'; + this._renderer.elem = svg.createElement('linearGradient', changed); + domElement.defs.appendChild(this._renderer.elem); + + // Otherwise apply all pending attributes + } else { + + svg.setAttributes(this._renderer.elem, changed); + + } + + if (this._flagStops) { + + var lengthChanged = this._renderer.elem.childNodes.length + !== this.stops.length; + + if (lengthChanged) { + this._renderer.elem.childNodes.length = 0; + } + + for (var i = 0; i < this.stops.length; i++) { + + var stop = this.stops[i]; + var attrs = {}; + + if (stop._flagOffset) { + attrs.offset = 100 * stop._offset + '%'; + } + if (stop._flagColor) { + attrs['stop-color'] = stop._color; + } + if (stop._flagOpacity) { + attrs['stop-opacity'] = stop._opacity; + } + + if (!stop._renderer.elem) { + stop._renderer.elem = svg.createElement('stop', attrs); + } else { + svg.setAttributes(stop._renderer.elem, attrs); + } + + if (lengthChanged) { + this._renderer.elem.appendChild(stop._renderer.elem); + } + stop.flagReset(); + + } + + } + + return this.flagReset(); + + } + + }, + + 'radial-gradient': { + + render: function(domElement, silent) { + + if (!silent) { + this._update(); + } + + var changed = {}; + + if (this._flagCenter) { + changed.cx = this.center._x; + changed.cy = this.center._y; + } + if (this._flagFocal) { + changed.fx = this.focal._x; + changed.fy = this.focal._y; + } + + if (this._flagRadius) { + changed.r = this._radius; + } + + if (this._flagSpread) { + changed.spreadMethod = this._spread; + } + + // If there is no attached DOM element yet, + // create it with all necessary attributes. + if (!this._renderer.elem) { + + changed.id = this.id; + changed.gradientUnits = 'userSpaceOnUse'; + this._renderer.elem = svg.createElement('radialGradient', changed); + domElement.defs.appendChild(this._renderer.elem); + + // Otherwise apply all pending attributes + } else { + + svg.setAttributes(this._renderer.elem, changed); + + } + + if (this._flagStops) { + + var lengthChanged = this._renderer.elem.childNodes.length + !== this.stops.length; + + if (lengthChanged) { + this._renderer.elem.childNodes.length = 0; + } + + for (var i = 0; i < this.stops.length; i++) { + + var stop = this.stops[i]; + var attrs = {}; + + if (stop._flagOffset) { + attrs.offset = 100 * stop._offset + '%'; + } + if (stop._flagColor) { + attrs['stop-color'] = stop._color; + } + if (stop._flagOpacity) { + attrs['stop-opacity'] = stop._opacity; + } + + if (!stop._renderer.elem) { + stop._renderer.elem = svg.createElement('stop', attrs); + } else { + svg.setAttributes(stop._renderer.elem, attrs); + } + + if (lengthChanged) { + this._renderer.elem.appendChild(stop._renderer.elem); + } + stop.flagReset(); + + } + + } + + return this.flagReset(); + + } + + }, + + texture: { + + render: function(domElement, silent) { + + if (!silent) { + this._update(); + } + + var changed = {}; + var styles = { x: 0, y: 0 }; + var image = this.image; + + if (this._flagLoaded && this.loaded) { + + switch (image.nodeName.toLowerCase()) { + + case 'canvas': + styles.href = styles['xlink:href'] = image.toDataURL('image/png'); + break; + case 'img': + case 'image': + styles.href = styles['xlink:href'] = this.src; + break; + + } + + } + + if (this._flagOffset || this._flagLoaded || this._flagScale) { + + changed.x = this._offset.x; + changed.y = this._offset.y; + + if (image) { + + changed.x -= image.width / 2; + changed.y -= image.height / 2; + + if (this._scale instanceof Two.Vector) { + changed.x *= this._scale.x; + changed.y *= this._scale.y; + } else { + changed.x *= this._scale; + changed.y *= this._scale; + } + } + + if (changed.x > 0) { + changed.x *= - 1; + } + if (changed.y > 0) { + changed.y *= - 1; + } + + } + + if (this._flagScale || this._flagLoaded || this._flagRepeat) { + + changed.width = 0; + changed.height = 0; + + if (image) { + + styles.width = changed.width = image.width; + styles.height = changed.height = image.height; + + // TODO: Hack / Bandaid + switch (this._repeat) { + case 'no-repeat': + changed.width += 1; + changed.height += 1; + break; + } + + if (this._scale instanceof Two.Vector) { + changed.width *= this._scale.x; + changed.height *= this._scale.y; + } else { + changed.width *= this._scale; + changed.height *= this._scale; + } + } + + } + + if (this._flagScale || this._flagLoaded) { + if (!this._renderer.image) { + this._renderer.image = svg.createElement('image', styles); + } else if (!_.isEmpty(styles)) { + svg.setAttributes(this._renderer.image, styles); + } + } + + if (!this._renderer.elem) { + + changed.id = this.id; + changed.patternUnits = 'userSpaceOnUse'; + this._renderer.elem = svg.createElement('pattern', changed); + domElement.defs.appendChild(this._renderer.elem); + + } else if (!_.isEmpty(changed)) { + + svg.setAttributes(this._renderer.elem, changed); + + } + + if (this._renderer.elem && this._renderer.image && !this._renderer.appended) { + this._renderer.elem.appendChild(this._renderer.image); + this._renderer.appended = true; + } + + return this.flagReset(); + + } + + } + + }; + + /** + * @class + */ + var Renderer = Two[Two.Types.svg] = function(params) { + + this.domElement = params.domElement || svg.createElement('svg'); + + this.scene = new Two.Group(); + this.scene.parent = this; + + this.defs = svg.createElement('defs'); + this.domElement.appendChild(this.defs); + this.domElement.defs = this.defs; + this.domElement.style.overflow = 'hidden'; + + }; + + _.extend(Renderer, { + + Utils: svg + + }); + + _.extend(Renderer.prototype, Two.Utils.Events, { + + setSize: function(width, height) { + + this.width = width; + this.height = height; + + svg.setAttributes(this.domElement, { + width: width, + height: height + }); + + return this; + + }, + + render: function() { + + svg.group.render.call(this.scene, this.domElement); + + return this; + + } + + }); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + /** + * Constants + */ + var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed; + var getRatio = Two.Utils.getRatio; + var _ = Two.Utils; + + // Returns true if this is a non-transforming matrix + var isDefaultMatrix = function (m) { + return (m[0] == 1 && m[3] == 0 && m[1] == 0 && m[4] == 1 && m[2] == 0 && m[5] == 0); + }; + + var canvas = { + + isHidden: /(none|transparent)/i, + + alignments: { + left: 'start', + middle: 'center', + right: 'end' + }, + + shim: function(elem) { + elem.tagName = 'canvas'; + elem.nodeType = 1; + return elem; + }, + + group: { + + renderChild: function(child) { + canvas[child._renderer.type].render.call(child, this.ctx, true, this.clip); + }, + + render: function(ctx) { + + // TODO: Add a check here to only invoke _update if need be. + this._update(); + + var matrix = this._matrix.elements; + var parent = this.parent; + this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1); + + var defaultMatrix = isDefaultMatrix(matrix); + + var mask = this._mask; + // var clip = this._clip; + + if (!this._renderer.context) { + this._renderer.context = {}; + } + + this._renderer.context.ctx = ctx; + // this._renderer.context.clip = clip; + + if (!defaultMatrix) { + ctx.save(); + ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); + } + + if (mask) { + canvas[mask._renderer.type].render.call(mask, ctx, true); + } + + if (this.opacity > 0 && this.scale !== 0) { + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + canvas[child._renderer.type].render.call(child, ctx); + } + } + + if (!defaultMatrix) { + ctx.restore(); + } + + /** + * Commented two-way functionality of clips / masks with groups and + * polygons. Uncomment when this bug is fixed: + * https://code.google.com/p/chromium/issues/detail?id=370951 + */ + + // if (clip) { + // ctx.clip(); + // } + + return this.flagReset(); + + } + + }, + + path: { + + render: function(ctx, forced, parentClipped) { + + var matrix, stroke, linewidth, fill, opacity, visible, cap, join, miter, + closed, commands, length, last, next, prev, a, b, c, d, ux, uy, vx, vy, + ar, bl, br, cl, x, y, mask, clip, defaultMatrix, isOffset; + + // TODO: Add a check here to only invoke _update if need be. + this._update(); + + matrix = this._matrix.elements; + stroke = this._stroke; + linewidth = this._linewidth; + fill = this._fill; + opacity = this._opacity * this.parent._renderer.opacity; + visible = this._visible; + cap = this._cap; + join = this._join; + miter = this._miter; + closed = this._closed; + commands = this._vertices; // Commands + length = commands.length; + last = length - 1; + defaultMatrix = isDefaultMatrix(matrix); + + // mask = this._mask; + clip = this._clip; + + if (!forced && (!visible || clip)) { + return this; + } + + // Transform + if (!defaultMatrix) { + ctx.save(); + ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); + } + + /** + * Commented two-way functionality of clips / masks with groups and + * polygons. Uncomment when this bug is fixed: + * https://code.google.com/p/chromium/issues/detail?id=370951 + */ + + // if (mask) { + // canvas[mask._renderer.type].render.call(mask, ctx, true); + // } + + // Styles + if (fill) { + if (_.isString(fill)) { + ctx.fillStyle = fill; + } else { + canvas[fill._renderer.type].render.call(fill, ctx); + ctx.fillStyle = fill._renderer.effect; + } + } + if (stroke) { + if (_.isString(stroke)) { + ctx.strokeStyle = stroke; + } else { + canvas[stroke._renderer.type].render.call(stroke, ctx); + ctx.strokeStyle = stroke._renderer.effect; + } + } + if (linewidth) { + ctx.lineWidth = linewidth; + } + if (miter) { + ctx.miterLimit = miter; + } + if (join) { + ctx.lineJoin = join; + } + if (cap) { + ctx.lineCap = cap; + } + if (_.isNumber(opacity)) { + ctx.globalAlpha = opacity; + } + + ctx.beginPath(); + + for (var i = 0; i < commands.length; i++) { + + b = commands[i]; + + x = toFixed(b._x); + y = toFixed(b._y); + + switch (b._command) { + + case Two.Commands.close: + ctx.closePath(); + break; + + case Two.Commands.curve: + + prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0); + next = closed ? mod(i + 1, length) : Math.min(i + 1, last); + + a = commands[prev]; + c = commands[next]; + ar = (a.controls && a.controls.right) || Two.Vector.zero; + bl = (b.controls && b.controls.left) || Two.Vector.zero; + + if (a._relative) { + vx = (ar.x + toFixed(a._x)); + vy = (ar.y + toFixed(a._y)); + } else { + vx = toFixed(ar.x); + vy = toFixed(ar.y); + } + + if (b._relative) { + ux = (bl.x + toFixed(b._x)); + uy = (bl.y + toFixed(b._y)); + } else { + ux = toFixed(bl.x); + uy = toFixed(bl.y); + } + + ctx.bezierCurveTo(vx, vy, ux, uy, x, y); + + if (i >= last && closed) { + + c = d; + + br = (b.controls && b.controls.right) || Two.Vector.zero; + cl = (c.controls && c.controls.left) || Two.Vector.zero; + + if (b._relative) { + vx = (br.x + toFixed(b._x)); + vy = (br.y + toFixed(b._y)); + } else { + vx = toFixed(br.x); + vy = toFixed(br.y); + } + + if (c._relative) { + ux = (cl.x + toFixed(c._x)); + uy = (cl.y + toFixed(c._y)); + } else { + ux = toFixed(cl.x); + uy = toFixed(cl.y); + } + + x = toFixed(c._x); + y = toFixed(c._y); + + ctx.bezierCurveTo(vx, vy, ux, uy, x, y); + + } + + break; + + case Two.Commands.line: + ctx.lineTo(x, y); + break; + + case Two.Commands.move: + d = b; + ctx.moveTo(x, y); + break; + + } + } + + // Loose ends + + if (closed) { + ctx.closePath(); + } + + if (!clip && !parentClipped) { + if (!canvas.isHidden.test(fill)) { + isOffset = fill._renderer && fill._renderer.offset + if (isOffset) { + ctx.save(); + ctx.translate( + - fill._renderer.offset.x, - fill._renderer.offset.y); + ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); + } + ctx.fill(); + if (isOffset) { + ctx.restore(); + } + } + if (!canvas.isHidden.test(stroke)) { + isOffset = stroke._renderer && stroke._renderer.offset; + if (isOffset) { + ctx.save(); + ctx.translate( + - stroke._renderer.offset.x, - stroke._renderer.offset.y); + ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); + ctx.lineWidth = linewidth / stroke._renderer.scale.x; + } + ctx.stroke(); + if (isOffset) { + ctx.restore(); + } + } + } + + if (!defaultMatrix) { + ctx.restore(); + } + + if (clip && !parentClipped) { + ctx.clip(); + } + + return this.flagReset(); + + } + + }, + + text: { + + render: function(ctx, forced, parentClipped) { + + // TODO: Add a check here to only invoke _update if need be. + this._update(); + + var matrix = this._matrix.elements; + var stroke = this._stroke; + var linewidth = this._linewidth; + var fill = this._fill; + var opacity = this._opacity * this.parent._renderer.opacity; + var visible = this._visible; + var defaultMatrix = isDefaultMatrix(matrix); + var isOffset = fill._renderer && fill._renderer.offset + && stroke._renderer && stroke._renderer.offset; + + var a, b, c, d, e, sx, sy; + + // mask = this._mask; + var clip = this._clip; + + if (!forced && (!visible || clip)) { + return this; + } + + // Transform + if (!defaultMatrix) { + ctx.save(); + ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); + } + + /** + * Commented two-way functionality of clips / masks with groups and + * polygons. Uncomment when this bug is fixed: + * https://code.google.com/p/chromium/issues/detail?id=370951 + */ + + // if (mask) { + // canvas[mask._renderer.type].render.call(mask, ctx, true); + // } + + if (!isOffset) { + ctx.font = [this._style, this._weight, this._size + 'px/' + + this._leading + 'px', this._family].join(' '); + } + + ctx.textAlign = canvas.alignments[this._alignment] || this._alignment; + ctx.textBaseline = this._baseline; + + // Styles + if (fill) { + if (_.isString(fill)) { + ctx.fillStyle = fill; + } else { + canvas[fill._renderer.type].render.call(fill, ctx); + ctx.fillStyle = fill._renderer.effect; + } + } + if (stroke) { + if (_.isString(stroke)) { + ctx.strokeStyle = stroke; + } else { + canvas[stroke._renderer.type].render.call(stroke, ctx); + ctx.strokeStyle = stroke._renderer.effect; + } + } + if (linewidth) { + ctx.lineWidth = linewidth; + } + if (_.isNumber(opacity)) { + ctx.globalAlpha = opacity; + } + + if (!clip && !parentClipped) { + + if (!canvas.isHidden.test(fill)) { + + if (fill._renderer && fill._renderer.offset) { + + sx = toFixed(fill._renderer.scale.x); + sy = toFixed(fill._renderer.scale.y); + + ctx.save(); + ctx.translate( - toFixed(fill._renderer.offset.x), + - toFixed(fill._renderer.offset.y)); + ctx.scale(sx, sy); + + a = this._size / fill._renderer.scale.y; + b = this._leading / fill._renderer.scale.y; + ctx.font = [this._style, this._weight, toFixed(a) + 'px/', + toFixed(b) + 'px', this._family].join(' '); + + c = fill._renderer.offset.x / fill._renderer.scale.x; + d = fill._renderer.offset.y / fill._renderer.scale.y; + + ctx.fillText(this.value, toFixed(c), toFixed(d)); + ctx.restore(); + + } else { + ctx.fillText(this.value, 0, 0); + } + + } + + if (!canvas.isHidden.test(stroke)) { + + if (stroke._renderer && stroke._renderer.offset) { + + sx = toFixed(stroke._renderer.scale.x); + sy = toFixed(stroke._renderer.scale.y); + + ctx.save(); + ctx.translate(- toFixed(stroke._renderer.offset.x), + - toFixed(stroke._renderer.offset.y)); + ctx.scale(sx, sy); + + a = this._size / stroke._renderer.scale.y; + b = this._leading / stroke._renderer.scale.y; + ctx.font = [this._style, this._weight, toFixed(a) + 'px/', + toFixed(b) + 'px', this._family].join(' '); + + c = stroke._renderer.offset.x / stroke._renderer.scale.x; + d = stroke._renderer.offset.y / stroke._renderer.scale.y; + e = linewidth / stroke._renderer.scale.x; + + ctx.lineWidth = toFixed(e); + ctx.strokeText(this.value, toFixed(c), toFixed(d)); + ctx.restore(); + + } else { + ctx.strokeText(this.value, 0, 0); + } + } + } + + if (!defaultMatrix) { + ctx.restore(); + } + + // TODO: Test for text + if (clip && !parentClipped) { + ctx.clip(); + } + + return this.flagReset(); + + } + + }, + + 'linear-gradient': { + + render: function(ctx) { + + this._update(); + + if (!this._renderer.effect || this._flagEndPoints || this._flagStops) { + + this._renderer.effect = ctx.createLinearGradient( + this.left._x, this.left._y, + this.right._x, this.right._y + ); + + for (var i = 0; i < this.stops.length; i++) { + var stop = this.stops[i]; + this._renderer.effect.addColorStop(stop._offset, stop._color); + } + + } + + return this.flagReset(); + + } + + }, + + 'radial-gradient': { + + render: function(ctx) { + + this._update(); + + if (!this._renderer.effect || this._flagCenter || this._flagFocal + || this._flagRadius || this._flagStops) { + + this._renderer.effect = ctx.createRadialGradient( + this.center._x, this.center._y, 0, + this.focal._x, this.focal._y, this._radius + ); + + for (var i = 0; i < this.stops.length; i++) { + var stop = this.stops[i]; + this._renderer.effect.addColorStop(stop._offset, stop._color); + } + + } + + return this.flagReset(); + + } + + }, + + texture: { + + render: function(ctx) { + + this._update(); + + var image = this.image; + var repeat; + + if (!this._renderer.effect || ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded)) { + this._renderer.effect = ctx.createPattern(this.image, this._repeat); + } + + if (this._flagOffset || this._flagLoaded || this._flagScale) { + + if (!(this._renderer.offset instanceof Two.Vector)) { + this._renderer.offset = new Two.Vector(); + } + + this._renderer.offset.x = - this._offset.x; + this._renderer.offset.y = - this._offset.y; + + if (image) { + + this._renderer.offset.x += image.width / 2; + this._renderer.offset.y += image.height / 2; + + if (this._scale instanceof Two.Vector) { + this._renderer.offset.x *= this._scale.x; + this._renderer.offset.y *= this._scale.y; + } else { + this._renderer.offset.x *= this._scale; + this._renderer.offset.y *= this._scale; + } + } + + } + + if (this._flagScale || this._flagLoaded) { + + if (!(this._renderer.scale instanceof Two.Vector)) { + this._renderer.scale = new Two.Vector(); + } + + if (this._scale instanceof Two.Vector) { + this._renderer.scale.copy(this._scale); + } else { + this._renderer.scale.set(this._scale, this._scale); + } + + } + + return this.flagReset(); + + } + + } + + }; + + var Renderer = Two[Two.Types.canvas] = function(params) { + // Smoothing property. Defaults to true + // Set it to false when working with pixel art. + // false can lead to better performance, since it would use a cheaper interpolation algorithm. + // It might not make a big difference on GPU backed canvases. + var smoothing = (params.smoothing !== false); + this.domElement = params.domElement || document.createElement('canvas'); + this.ctx = this.domElement.getContext('2d'); + this.overdraw = params.overdraw || false; + + if (!_.isUndefined(this.ctx.imageSmoothingEnabled)) { + this.ctx.imageSmoothingEnabled = smoothing; + } + + // Everything drawn on the canvas needs to be added to the scene. + this.scene = new Two.Group(); + this.scene.parent = this; + }; + + + _.extend(Renderer, { + + Utils: canvas + + }); + + _.extend(Renderer.prototype, Two.Utils.Events, { + + setSize: function(width, height, ratio) { + + this.width = width; + this.height = height; + + this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio; + + this.domElement.width = width * this.ratio; + this.domElement.height = height * this.ratio; + + if (this.domElement.style) { + _.extend(this.domElement.style, { + width: width + 'px', + height: height + 'px' + }); + } + + return this; + + }, + + render: function() { + + var isOne = this.ratio === 1; + + if (!isOne) { + this.ctx.save(); + this.ctx.scale(this.ratio, this.ratio); + } + + if (!this.overdraw) { + this.ctx.clearRect(0, 0, this.width, this.height); + } + + canvas.group.render.call(this.scene, this.ctx); + + if (!isOne) { + this.ctx.restore(); + } + + return this; + + } + + }); + + function resetTransform(ctx) { + ctx.setTransform(1, 0, 0, 1, 0, 0); + } + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + /** + * Constants + */ + + var root = Two.root, + multiplyMatrix = Two.Matrix.Multiply, + mod = Two.Utils.mod, + identity = [1, 0, 0, 0, 1, 0, 0, 0, 1], + transformation = new Two.Array(9), + getRatio = Two.Utils.getRatio, + getComputedMatrix = Two.Utils.getComputedMatrix, + toFixed = Two.Utils.toFixed, + _ = Two.Utils; + + var webgl = { + + isHidden: /(none|transparent)/i, + + canvas: (root.document ? root.document.createElement('canvas') : { getContext: _.identity }), + + alignments: { + left: 'start', + middle: 'center', + right: 'end' + }, + + matrix: new Two.Matrix(), + + uv: new Two.Array([ + 0, 0, + 1, 0, + 0, 1, + 0, 1, + 1, 0, + 1, 1 + ]), + + group: { + + removeChild: function(child, gl) { + if (child.children) { + for (var i = 0; i < child.children.length; i++) { + webgl.group.removeChild(child.children[i], gl); + } + return; + } + // Deallocate texture to free up gl memory. + gl.deleteTexture(child._renderer.texture); + delete child._renderer.texture; + }, + + renderChild: function(child) { + webgl[child._renderer.type].render.call(child, this.gl, this.program); + }, + + render: function(gl, program) { + + this._update(); + + var parent = this.parent; + var flagParentMatrix = (parent._matrix && parent._matrix.manual) || parent._flagMatrix; + var flagMatrix = this._matrix.manual || this._flagMatrix; + + if (flagParentMatrix || flagMatrix) { + + if (!this._renderer.matrix) { + this._renderer.matrix = new Two.Array(9); + } + + // Reduce amount of object / array creation / deletion + this._matrix.toArray(true, transformation); + + multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); + this._renderer.scale = this._scale * parent._renderer.scale; + + if (flagParentMatrix) { + this._flagMatrix = true; + } + + } + + if (this._mask) { + + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS, 1, 1); + + gl.colorMask(false, false, false, true); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); + + webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + + } + + this._flagOpacity = parent._flagOpacity || this._flagOpacity; + + this._renderer.opacity = this._opacity + * (parent && parent._renderer ? parent._renderer.opacity : 1); + + if (this._flagSubtractions) { + for (var i = 0; i < this.subtractions.length; i++) { + webgl.group.removeChild(this.subtractions[i], gl); + } + } + + this.children.forEach(webgl.group.renderChild, { + gl: gl, + program: program + }); + + if (this._mask) { + + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); + + webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, 1); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + + gl.disable(gl.STENCIL_TEST); + + } + + return this.flagReset(); + + } + + }, + + path: { + + updateCanvas: function(elem) { + + var next, prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y; + var isOffset; + + var commands = elem._vertices; + var canvas = this.canvas; + var ctx = this.ctx; + + // Styles + var scale = elem._renderer.scale; + var stroke = elem._stroke; + var linewidth = elem._linewidth; + var fill = elem._fill; + var opacity = elem._renderer.opacity || elem._opacity; + var cap = elem._cap; + var join = elem._join; + var miter = elem._miter; + var closed = elem._closed; + var length = commands.length; + var last = length - 1; + + canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1); + canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1); + + var centroid = elem._renderer.rect.centroid; + var cx = centroid.x; + var cy = centroid.y; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + if (fill) { + if (_.isString(fill)) { + ctx.fillStyle = fill; + } else { + webgl[fill._renderer.type].render.call(fill, ctx, elem); + ctx.fillStyle = fill._renderer.effect; + } + } + if (stroke) { + if (_.isString(stroke)) { + ctx.strokeStyle = stroke; + } else { + webgl[stroke._renderer.type].render.call(stroke, ctx, elem); + ctx.strokeStyle = stroke._renderer.effect; + } + } + if (linewidth) { + ctx.lineWidth = linewidth; + } + if (miter) { + ctx.miterLimit = miter; + } + if (join) { + ctx.lineJoin = join; + } + if (cap) { + ctx.lineCap = cap; + } + if (_.isNumber(opacity)) { + ctx.globalAlpha = opacity; + } + + var d; + ctx.save(); + ctx.scale(scale, scale); + ctx.translate(cx, cy); + + ctx.beginPath(); + for (var i = 0; i < commands.length; i++) { + + b = commands[i]; + + x = toFixed(b._x); + y = toFixed(b._y); + + switch (b._command) { + + case Two.Commands.close: + ctx.closePath(); + break; + + case Two.Commands.curve: + + prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0); + next = closed ? mod(i + 1, length) : Math.min(i + 1, last); + + a = commands[prev]; + c = commands[next]; + ar = (a.controls && a.controls.right) || Two.Vector.zero; + bl = (b.controls && b.controls.left) || Two.Vector.zero; + + if (a._relative) { + vx = toFixed((ar.x + a._x)); + vy = toFixed((ar.y + a._y)); + } else { + vx = toFixed(ar.x); + vy = toFixed(ar.y); + } + + if (b._relative) { + ux = toFixed((bl.x + b._x)); + uy = toFixed((bl.y + b._y)); + } else { + ux = toFixed(bl.x); + uy = toFixed(bl.y); + } + + ctx.bezierCurveTo(vx, vy, ux, uy, x, y); + + if (i >= last && closed) { + + c = d; + + br = (b.controls && b.controls.right) || Two.Vector.zero; + cl = (c.controls && c.controls.left) || Two.Vector.zero; + + if (b._relative) { + vx = toFixed((br.x + b._x)); + vy = toFixed((br.y + b._y)); + } else { + vx = toFixed(br.x); + vy = toFixed(br.y); + } + + if (c._relative) { + ux = toFixed((cl.x + c._x)); + uy = toFixed((cl.y + c._y)); + } else { + ux = toFixed(cl.x); + uy = toFixed(cl.y); + } + + x = toFixed(c._x); + y = toFixed(c._y); + + ctx.bezierCurveTo(vx, vy, ux, uy, x, y); + + } + + break; + + case Two.Commands.line: + ctx.lineTo(x, y); + break; + + case Two.Commands.move: + d = b; + ctx.moveTo(x, y); + break; + + } + + } + + // Loose ends + + if (closed) { + ctx.closePath(); + } + + if (!webgl.isHidden.test(fill)) { + isOffset = fill._renderer && fill._renderer.offset + if (isOffset) { + ctx.save(); + ctx.translate( + - fill._renderer.offset.x, - fill._renderer.offset.y); + ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); + } + ctx.fill(); + if (isOffset) { + ctx.restore(); + } + } + + if (!webgl.isHidden.test(stroke)) { + isOffset = stroke._renderer && stroke._renderer.offset; + if (isOffset) { + ctx.save(); + ctx.translate( + - stroke._renderer.offset.x, - stroke._renderer.offset.y); + ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); + ctx.lineWidth = linewidth / stroke._renderer.scale.x; + } + ctx.stroke(); + if (isOffset) { + ctx.restore(); + } + } + + ctx.restore(); + + }, + + /** + * Returns the rect of a set of verts. Typically takes vertices that are + * "centered" around 0 and returns them to be anchored upper-left. + */ + getBoundingClientRect: function(vertices, border, rect) { + + var left = Infinity, right = -Infinity, + top = Infinity, bottom = -Infinity, + width, height; + + vertices.forEach(function(v) { + + var x = v.x, y = v.y, controls = v.controls; + var a, b, c, d, cl, cr; + + top = Math.min(y, top); + left = Math.min(x, left); + right = Math.max(x, right); + bottom = Math.max(y, bottom); + + if (!v.controls) { + return; + } + + cl = controls.left; + cr = controls.right; + + if (!cl || !cr) { + return; + } + + a = v._relative ? cl.x + x : cl.x; + b = v._relative ? cl.y + y : cl.y; + c = v._relative ? cr.x + x : cr.x; + d = v._relative ? cr.y + y : cr.y; + + if (!a || !b || !c || !d) { + return; + } + + top = Math.min(b, d, top); + left = Math.min(a, c, left); + right = Math.max(a, c, right); + bottom = Math.max(b, d, bottom); + + }); + + // Expand borders + + if (_.isNumber(border)) { + top -= border; + left -= border; + right += border; + bottom += border; + } + + width = right - left; + height = bottom - top; + + rect.top = top; + rect.left = left; + rect.right = right; + rect.bottom = bottom; + rect.width = width; + rect.height = height; + + if (!rect.centroid) { + rect.centroid = {}; + } + + rect.centroid.x = - left; + rect.centroid.y = - top; + + }, + + render: function(gl, program, forcedParent) { + + if (!this._visible || !this._opacity) { + return this; + } + + this._update(); + + // Calculate what changed + + var parent = this.parent; + var flagParentMatrix = parent._matrix.manual || parent._flagMatrix; + var flagMatrix = this._matrix.manual || this._flagMatrix; + var flagTexture = this._flagVertices || this._flagFill + || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints)) + || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal)) + || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagOffset || this._fill._flagScale)) + || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints)) + || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal)) + || (this._stroke instanceof Two.Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagOffset || this._fill._flagScale)) + || this._flagStroke || this._flagLinewidth || this._flagOpacity + || parent._flagOpacity || this._flagVisible || this._flagCap + || this._flagJoin || this._flagMiter || this._flagScale + || !this._renderer.texture; + + if (flagParentMatrix || flagMatrix) { + + if (!this._renderer.matrix) { + this._renderer.matrix = new Two.Array(9); + } + + // Reduce amount of object / array creation / deletion + + this._matrix.toArray(true, transformation); + + multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); + this._renderer.scale = this._scale * parent._renderer.scale; + + } + + if (flagTexture) { + + if (!this._renderer.rect) { + this._renderer.rect = {}; + } + + if (!this._renderer.triangles) { + this._renderer.triangles = new Two.Array(12); + } + + this._renderer.opacity = this._opacity * parent._renderer.opacity; + + webgl.path.getBoundingClientRect(this._vertices, this._linewidth, this._renderer.rect); + webgl.getTriangles(this._renderer.rect, this._renderer.triangles); + + webgl.updateBuffer.call(webgl, gl, this, program); + webgl.updateTexture.call(webgl, gl, this); + + } + + // if (this._mask) { + // webgl[this._mask._renderer.type].render.call(mask, gl, program, this); + // } + + if (this._clip && !forcedParent) { + return; + } + + // Draw Texture + + gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer); + + gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0); + + gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); + + + // Draw Rect + + gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer); + + gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + + return this.flagReset(); + + } + + }, + + text: { + + updateCanvas: function(elem) { + + var canvas = this.canvas; + var ctx = this.ctx; + + // Styles + var scale = elem._renderer.scale; + var stroke = elem._stroke; + var linewidth = elem._linewidth * scale; + var fill = elem._fill; + var opacity = elem._renderer.opacity || elem._opacity; + + canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1); + canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1); + + var centroid = elem._renderer.rect.centroid; + var cx = centroid.x; + var cy = centroid.y; + + var a, b, c, d, e, sx, sy; + var isOffset = fill._renderer && fill._renderer.offset + && stroke._renderer && stroke._renderer.offset; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + + if (!isOffset) { + ctx.font = [elem._style, elem._weight, elem._size + 'px/' + + elem._leading + 'px', elem._family].join(' '); + } + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + // Styles + if (fill) { + if (_.isString(fill)) { + ctx.fillStyle = fill; + } else { + webgl[fill._renderer.type].render.call(fill, ctx, elem); + ctx.fillStyle = fill._renderer.effect; + } + } + if (stroke) { + if (_.isString(stroke)) { + ctx.strokeStyle = stroke; + } else { + webgl[stroke._renderer.type].render.call(stroke, ctx, elem); + ctx.strokeStyle = stroke._renderer.effect; + } + } + if (linewidth) { + ctx.lineWidth = linewidth; + } + if (_.isNumber(opacity)) { + ctx.globalAlpha = opacity; + } + + ctx.save(); + ctx.scale(scale, scale); + ctx.translate(cx, cy); + + if (!webgl.isHidden.test(fill)) { + + if (fill._renderer && fill._renderer.offset) { + + sx = toFixed(fill._renderer.scale.x); + sy = toFixed(fill._renderer.scale.y); + + ctx.save(); + ctx.translate( - toFixed(fill._renderer.offset.x), + - toFixed(fill._renderer.offset.y)); + ctx.scale(sx, sy); + + a = elem._size / fill._renderer.scale.y; + b = elem._leading / fill._renderer.scale.y; + ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/', + toFixed(b) + 'px', elem._family].join(' '); + + c = fill._renderer.offset.x / fill._renderer.scale.x; + d = fill._renderer.offset.y / fill._renderer.scale.y; + + ctx.fillText(elem.value, toFixed(c), toFixed(d)); + ctx.restore(); + + } else { + ctx.fillText(elem.value, 0, 0); + } + + } + + if (!webgl.isHidden.test(stroke)) { + + if (stroke._renderer && stroke._renderer.offset) { + + sx = toFixed(stroke._renderer.scale.x); + sy = toFixed(stroke._renderer.scale.y); + + ctx.save(); + ctx.translate(- toFixed(stroke._renderer.offset.x), + - toFixed(stroke._renderer.offset.y)); + ctx.scale(sx, sy); + + a = elem._size / stroke._renderer.scale.y; + b = elem._leading / stroke._renderer.scale.y; + ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/', + toFixed(b) + 'px', elem._family].join(' '); + + c = stroke._renderer.offset.x / stroke._renderer.scale.x; + d = stroke._renderer.offset.y / stroke._renderer.scale.y; + e = linewidth / stroke._renderer.scale.x; + + ctx.lineWidth = toFixed(e); + ctx.strokeText(elem.value, toFixed(c), toFixed(d)); + ctx.restore(); + + } else { + ctx.strokeText(elem.value, 0, 0); + } + + } + + ctx.restore(); + + }, + + getBoundingClientRect: function(elem, rect) { + + var ctx = webgl.ctx; + + ctx.font = [elem._style, elem._weight, elem._size + 'px/' + + elem._leading + 'px', elem._family].join(' '); + + ctx.textAlign = 'center'; + ctx.textBaseline = elem._baseline; + + // TODO: Estimate this better + var width = ctx.measureText(elem._value).width; + var height = Math.max(elem._size || elem._leading); + + if (this._linewidth && !webgl.isHidden.test(this._stroke)) { + // width += this._linewidth; // TODO: Not sure if the `measure` calcs this. + height += this._linewidth; + } + + var w = width / 2; + var h = height / 2; + + switch (webgl.alignments[elem._alignment] || elem._alignment) { + + case webgl.alignments.left: + rect.left = 0; + rect.right = width; + break; + case webgl.alignments.right: + rect.left = - width; + rect.right = 0; + break; + default: + rect.left = - w; + rect.right = w; + } + + // TODO: Gradients aren't inherited... + switch (elem._baseline) { + case 'bottom': + rect.top = - height; + rect.bottom = 0; + break; + case 'top': + rect.top = 0; + rect.bottom = height; + break; + default: + rect.top = - h; + rect.bottom = h; + } + + rect.width = width; + rect.height = height; + + if (!rect.centroid) { + rect.centroid = {}; + } + + // TODO: + rect.centroid.x = w; + rect.centroid.y = h; + + }, + + render: function(gl, program, forcedParent) { + + if (!this._visible || !this._opacity) { + return this; + } + + this._update(); + + // Calculate what changed + + var parent = this.parent; + var flagParentMatrix = parent._matrix.manual || parent._flagMatrix; + var flagMatrix = this._matrix.manual || this._flagMatrix; + var flagTexture = this._flagVertices || this._flagFill + || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints)) + || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal)) + || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded)) + || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints)) + || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal)) + || (this._texture instanceof Two.Texture && (this._texture._flagLoaded && this._texture.loaded)) + || this._flagStroke || this._flagLinewidth || this._flagOpacity + || parent._flagOpacity || this._flagVisible || this._flagScale + || this._flagValue || this._flagFamily || this._flagSize + || this._flagLeading || this._flagAlignment || this._flagBaseline + || this._flagStyle || this._flagWeight || this._flagDecoration + || !this._renderer.texture; + + if (flagParentMatrix || flagMatrix) { + + if (!this._renderer.matrix) { + this._renderer.matrix = new Two.Array(9); + } + + // Reduce amount of object / array creation / deletion + + this._matrix.toArray(true, transformation); + + multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); + this._renderer.scale = this._scale * parent._renderer.scale; + + } + + if (flagTexture) { + + if (!this._renderer.rect) { + this._renderer.rect = {}; + } + + if (!this._renderer.triangles) { + this._renderer.triangles = new Two.Array(12); + } + + this._renderer.opacity = this._opacity * parent._renderer.opacity; + + webgl.text.getBoundingClientRect(this, this._renderer.rect); + webgl.getTriangles(this._renderer.rect, this._renderer.triangles); + + webgl.updateBuffer.call(webgl, gl, this, program); + webgl.updateTexture.call(webgl, gl, this); + + } + + // if (this._mask) { + // webgl[this._mask._renderer.type].render.call(mask, gl, program, this); + // } + + if (this._clip && !forcedParent) { + return; + } + + // Draw Texture + + gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer); + + gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0); + + gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); + + + // Draw Rect + + gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer); + + gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + + return this.flagReset(); + + } + + }, + + 'linear-gradient': { + + render: function(ctx, elem) { + + if (!ctx.canvas.getContext('2d')) { + return; + } + + this._update(); + + if (!this._renderer.effect || this._flagEndPoints || this._flagStops) { + + this._renderer.effect = ctx.createLinearGradient( + this.left._x, this.left._y, + this.right._x, this.right._y + ); + + for (var i = 0; i < this.stops.length; i++) { + var stop = this.stops[i]; + this._renderer.effect.addColorStop(stop._offset, stop._color); + } + + } + + return this.flagReset(); + + } + + }, + + 'radial-gradient': { + + render: function(ctx, elem) { + + if (!ctx.canvas.getContext('2d')) { + return; + } + + this._update(); + + if (!this._renderer.effect || this._flagCenter || this._flagFocal + || this._flagRadius || this._flagStops) { + + this._renderer.effect = ctx.createRadialGradient( + this.center._x, this.center._y, 0, + this.focal._x, this.focal._y, this._radius + ); + + for (var i = 0; i < this.stops.length; i++) { + var stop = this.stops[i]; + this._renderer.effect.addColorStop(stop._offset, stop._color); + } + + } + + return this.flagReset(); + + } + + }, + + texture: { + + render: function(ctx, elem) { + + if (!ctx.canvas.getContext('2d')) { + return; + } + + this._update(); + + var image = this.image; + var repeat; + + if (!this._renderer.effect || ((this._flagLoaded || this._flagRepeat) && this.loaded)) { + this._renderer.effect = ctx.createPattern(image, this._repeat); + } + + if (this._flagOffset || this._flagLoaded || this._flagScale) { + + if (!(this._renderer.offset instanceof Two.Vector)) { + this._renderer.offset = new Two.Vector(); + } + + this._renderer.offset.x = this._offset.x; + this._renderer.offset.y = this._offset.y; + + if (image) { + + this._renderer.offset.x -= image.width / 2; + this._renderer.offset.y += image.height / 2; + + if (this._scale instanceof Two.Vector) { + this._renderer.offset.x *= this._scale.x; + this._renderer.offset.y *= this._scale.y; + } else { + this._renderer.offset.x *= this._scale; + this._renderer.offset.y *= this._scale; + } + } + + } + + if (this._flagScale || this._flagLoaded) { + + if (!(this._renderer.scale instanceof Two.Vector)) { + this._renderer.scale = new Two.Vector(); + } + + if (this._scale instanceof Two.Vector) { + this._renderer.scale.copy(this._scale); + } else { + this._renderer.scale.set(this._scale, this._scale); + } + + } + + return this.flagReset(); + + } + + }, + + getTriangles: function(rect, triangles) { + + var top = rect.top, + left = rect.left, + right = rect.right, + bottom = rect.bottom; + + // First Triangle + + triangles[0] = left; + triangles[1] = top; + + triangles[2] = right; + triangles[3] = top; + + triangles[4] = left; + triangles[5] = bottom; + + // Second Triangle + + triangles[6] = left; + triangles[7] = bottom; + + triangles[8] = right; + triangles[9] = top; + + triangles[10] = right; + triangles[11] = bottom; + + }, + + updateTexture: function(gl, elem) { + + this[elem._renderer.type].updateCanvas.call(webgl, elem); + + if (elem._renderer.texture) { + gl.deleteTexture(elem._renderer.texture); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer); + + // TODO: Is this necessary every time or can we do once? + // TODO: Create a registry for textures + elem._renderer.texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture); + + // Set the parameters so we can render any size image. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + if (this.canvas.width <= 0 || this.canvas.height <= 0) { + return; + } + + // Upload the image into the texture. + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas); + + }, + + updateBuffer: function(gl, elem, program) { + + if (_.isObject(elem._renderer.buffer)) { + gl.deleteBuffer(elem._renderer.buffer); + } + + elem._renderer.buffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.buffer); + gl.enableVertexAttribArray(program.position); + + gl.bufferData(gl.ARRAY_BUFFER, elem._renderer.triangles, gl.STATIC_DRAW); + + if (_.isObject(elem._renderer.textureCoordsBuffer)) { + gl.deleteBuffer(elem._renderer.textureCoordsBuffer); + } + + elem._renderer.textureCoordsBuffer = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer); + gl.enableVertexAttribArray(program.textureCoords); + + gl.bufferData(gl.ARRAY_BUFFER, this.uv, gl.STATIC_DRAW); + + }, + + program: { + + create: function(gl, shaders) { + var program, linked, error; + program = gl.createProgram(); + _.each(shaders, function(s) { + gl.attachShader(program, s); + }); + + gl.linkProgram(program); + linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + error = gl.getProgramInfoLog(program); + gl.deleteProgram(program); + throw new Two.Utils.Error('unable to link program: ' + error); + } + + return program; + + } + + }, + + shaders: { + + create: function(gl, source, type) { + var shader, compiled, error; + shader = gl.createShader(gl[type]); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + if (!compiled) { + error = gl.getShaderInfoLog(shader); + gl.deleteShader(shader); + throw new Two.Utils.Error('unable to compile shader ' + shader + ': ' + error); + } + + return shader; + + }, + + types: { + vertex: 'VERTEX_SHADER', + fragment: 'FRAGMENT_SHADER' + }, + + vertex: [ + 'attribute vec2 a_position;', + 'attribute vec2 a_textureCoords;', + '', + 'uniform mat3 u_matrix;', + 'uniform vec2 u_resolution;', + '', + 'varying vec2 v_textureCoords;', + '', + 'void main() {', + ' vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;', + ' vec2 normal = projected / u_resolution;', + ' vec2 clipspace = (normal * 2.0) - 1.0;', + '', + ' gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);', + ' v_textureCoords = a_textureCoords;', + '}' + ].join('\n'), + + fragment: [ + 'precision mediump float;', + '', + 'uniform sampler2D u_image;', + 'varying vec2 v_textureCoords;', + '', + 'void main() {', + ' gl_FragColor = texture2D(u_image, v_textureCoords);', + '}' + ].join('\n') + + }, + + TextureRegistry: new Two.Registry() + + }; + + webgl.ctx = webgl.canvas.getContext('2d'); + + var Renderer = Two[Two.Types.webgl] = function(options) { + + var params, gl, vs, fs; + this.domElement = options.domElement || document.createElement('canvas'); + + // Everything drawn on the canvas needs to come from the stage. + this.scene = new Two.Group(); + this.scene.parent = this; + + this._renderer = { + matrix: new Two.Array(identity), + scale: 1, + opacity: 1 + }; + this._flagMatrix = true; + + // http://games.greggman.com/game/webgl-and-alpha/ + // http://www.khronos.org/registry/webgl/specs/latest/#5.2 + params = _.defaults(options || {}, { + antialias: false, + alpha: true, + premultipliedAlpha: true, + stencil: true, + preserveDrawingBuffer: true, + overdraw: false + }); + + this.overdraw = params.overdraw; + + gl = this.ctx = this.domElement.getContext('webgl', params) || + this.domElement.getContext('experimental-webgl', params); + + if (!this.ctx) { + throw new Two.Utils.Error( + 'unable to create a webgl context. Try using another renderer.'); + } + + // Compile Base Shaders to draw in pixel space. + vs = webgl.shaders.create( + gl, webgl.shaders.vertex, webgl.shaders.types.vertex); + fs = webgl.shaders.create( + gl, webgl.shaders.fragment, webgl.shaders.types.fragment); + + this.program = webgl.program.create(gl, [vs, fs]); + gl.useProgram(this.program); + + // Create and bind the drawing buffer + + // look up where the vertex data needs to go. + this.program.position = gl.getAttribLocation(this.program, 'a_position'); + this.program.matrix = gl.getUniformLocation(this.program, 'u_matrix'); + this.program.textureCoords = gl.getAttribLocation(this.program, 'a_textureCoords'); + + // Copied from Three.js WebGLRenderer + gl.disable(gl.DEPTH_TEST); + + // Setup some initial statements of the gl context + gl.enable(gl.BLEND); + + // https://code.google.com/p/chromium/issues/detail?id=316393 + // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, gl.TRUE); + + gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD); + gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, + gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + + }; + + _.extend(Renderer, { + + Utils: webgl + + }); + + _.extend(Renderer.prototype, Two.Utils.Events, { + + setSize: function(width, height, ratio) { + + this.width = width; + this.height = height; + + this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio; + + this.domElement.width = width * this.ratio; + this.domElement.height = height * this.ratio; + + _.extend(this.domElement.style, { + width: width + 'px', + height: height + 'px' + }); + + width *= this.ratio; + height *= this.ratio; + + // Set for this.stage parent scaling to account for HDPI + this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio; + + this._flagMatrix = true; + + this.ctx.viewport(0, 0, width, height); + + var resolutionLocation = this.ctx.getUniformLocation( + this.program, 'u_resolution'); + this.ctx.uniform2f(resolutionLocation, width, height); + + return this; + + }, + + render: function() { + + var gl = this.ctx; + + if (!this.overdraw) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + webgl.group.render.call(this.scene, gl, this.program); + this._flagMatrix = false; + + return this; + + } + + }); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var Shape = Two.Shape = function() { + + // Private object for renderer specific variables. + this._renderer = {}; + this._renderer.flagMatrix = _.bind(Shape.FlagMatrix, this); + this.isShape = true; + + this.id = Two.Identifier + Two.uniqueId(); + this.classList = []; + + // Define matrix properties which all inherited + // objects of Shape have. + + this._matrix = new Two.Matrix(); + + this.translation = new Two.Vector(); + this.rotation = 0; + this.scale = 1; + + }; + + _.extend(Shape, { + + FlagMatrix: function() { + this._flagMatrix = true; + }, + + MakeObservable: function(object) { + + Object.defineProperty(object, 'translation', { + enumerable: true, + get: function() { + return this._translation; + }, + set: function(v) { + if (this._translation) { + this._translation.unbind(Two.Events.change, this._renderer.flagMatrix); + } + this._translation = v; + this._translation.bind(Two.Events.change, this._renderer.flagMatrix); + Shape.FlagMatrix.call(this); + } + }); + + Object.defineProperty(object, 'rotation', { + enumerable: true, + get: function() { + return this._rotation; + }, + set: function(v) { + this._rotation = v; + this._flagMatrix = true; + } + }); + + Object.defineProperty(object, 'scale', { + enumerable: true, + get: function() { + return this._scale; + }, + set: function(v) { + + if (this._scale instanceof Two.Vector) { + this._scale.unbind(Two.Events.change, this._renderer.flagMatrix); + } + + this._scale = v; + + if (this._scale instanceof Two.Vector) { + this._scale.bind(Two.Events.change, this._renderer.flagMatrix); + } + + this._flagMatrix = true; + this._flagScale = true; + + } + }); + + } + + }); + + _.extend(Shape.prototype, Two.Utils.Events, { + + // Flags + + _flagMatrix: true, + _flagScale: false, + + // _flagMask: false, + // _flagClip: false, + + // Underlying Properties + + _rotation: 0, + _scale: 1, + _translation: null, + + // _mask: null, + // _clip: false, + + addTo: function(group) { + group.add(this); + return this; + }, + + clone: function() { + var clone = new Shape(); + clone.translation.copy(this.translation); + clone.rotation = this.rotation; + clone.scale = this.scale; + _.each(Shape.Properties, function(k) { + clone[k] = this[k]; + }, this); + return clone._update(); + }, + + /** + * To be called before render that calculates and collates all information + * to be as up-to-date as possible for the render. Called once a frame. + */ + _update: function(deep) { + + if (!this._matrix.manual && this._flagMatrix) { + + this._matrix + .identity() + .translate(this.translation.x, this.translation.y); + + if (this._scale instanceof Two.Vector) { + this._matrix.scale(this._scale.x, this._scale.y); + } else { + this._matrix.scale(this._scale); + } + + this._matrix.rotate(this.rotation); + + } + + if (deep) { + // Bubble up to parents mainly for `getBoundingClientRect` method. + if (this.parent && this.parent._update) { + this.parent._update(); + } + } + + return this; + + }, + + flagReset: function() { + + this._flagMatrix = this._flagScale = false; + + return this; + + } + + }); + + Shape.MakeObservable(Shape.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + /** + * Constants + */ + + var min = Math.min, max = Math.max, round = Math.round, + getComputedMatrix = Two.Utils.getComputedMatrix; + + var commands = {}; + var _ = Two.Utils; + + _.each(Two.Commands, function(v, k) { + commands[k] = new RegExp(v); + }); + + var Path = Two.Path = function(vertices, closed, curved, manual) { + + Two.Shape.call(this); + + this._renderer.type = 'path'; + this._renderer.flagVertices = _.bind(Path.FlagVertices, this); + this._renderer.bindVertices = _.bind(Path.BindVertices, this); + this._renderer.unbindVertices = _.bind(Path.UnbindVertices, this); + + this._renderer.flagFill = _.bind(Path.FlagFill, this); + this._renderer.flagStroke = _.bind(Path.FlagStroke, this); + + this._closed = !!closed; + this._curved = !!curved; + + this.beginning = 0; + this.ending = 1; + + // Style properties + + this.fill = '#fff'; + this.stroke = '#000'; + this.linewidth = 1.0; + this.opacity = 1.0; + this.visible = true; + + this.cap = 'butt'; // Default of Adobe Illustrator + this.join = 'miter'; // Default of Adobe Illustrator + this.miter = 4; // Default of Adobe Illustrator + + this._vertices = []; + this.vertices = vertices; + + // Determines whether or not two.js should calculate curves, lines, and + // commands automatically for you or to let the developer manipulate them + // for themselves. + this.automatic = !manual; + + }; + + _.extend(Path, { + + Properties: [ + 'fill', + 'stroke', + 'linewidth', + 'opacity', + 'visible', + 'cap', + 'join', + 'miter', + + 'closed', + 'curved', + 'automatic', + 'beginning', + 'ending' + ], + + FlagVertices: function() { + this._flagVertices = true; + this._flagLength = true; + }, + + BindVertices: function(items) { + + // This function is called a lot + // when importing a large SVG + var i = items.length; + while (i--) { + items[i].bind(Two.Events.change, this._renderer.flagVertices); + } + + this._renderer.flagVertices(); + + }, + + UnbindVertices: function(items) { + + var i = items.length; + while (i--) { + items[i].unbind(Two.Events.change, this._renderer.flagVertices); + } + + this._renderer.flagVertices(); + + }, + + FlagFill: function() { + this._flagFill = true; + }, + + FlagStroke: function() { + this._flagStroke = true; + }, + + MakeObservable: function(object) { + + Two.Shape.MakeObservable(object); + + // Only the 6 defined properties are flagged like this. The subsequent + // properties behave differently and need to be hand written. + _.each(Path.Properties.slice(2, 8), Two.Utils.defineProperty, object); + + Object.defineProperty(object, 'fill', { + enumerable: true, + get: function() { + return this._fill; + }, + set: function(f) { + + if (this._fill instanceof Two.Gradient + || this._fill instanceof Two.LinearGradient + || this._fill instanceof Two.RadialGradient + || this._fill instanceof Two.Texture) { + this._fill.unbind(Two.Events.change, this._renderer.flagFill); + } + + this._fill = f; + this._flagFill = true; + + if (this._fill instanceof Two.Gradient + || this._fill instanceof Two.LinearGradient + || this._fill instanceof Two.RadialGradient + || this._fill instanceof Two.Texture) { + this._fill.bind(Two.Events.change, this._renderer.flagFill); + } + + } + }); + + Object.defineProperty(object, 'stroke', { + enumerable: true, + get: function() { + return this._stroke; + }, + set: function(f) { + + if (this._stroke instanceof Two.Gradient + || this._stroke instanceof Two.LinearGradient + || this._stroke instanceof Two.RadialGradient + || this._stroke instanceof Two.Texture) { + this._stroke.unbind(Two.Events.change, this._renderer.flagStroke); + } + + this._stroke = f; + this._flagStroke = true; + + if (this._stroke instanceof Two.Gradient + || this._stroke instanceof Two.LinearGradient + || this._stroke instanceof Two.RadialGradient + || this._stroke instanceof Two.Texture) { + this._stroke.bind(Two.Events.change, this._renderer.flagStroke); + } + + } + }); + + Object.defineProperty(object, 'length', { + get: function() { + if (this._flagLength) { + this._updateLength(); + } + return this._length; + } + }); + + Object.defineProperty(object, 'closed', { + enumerable: true, + get: function() { + return this._closed; + }, + set: function(v) { + this._closed = !!v; + this._flagVertices = true; + } + }); + + Object.defineProperty(object, 'curved', { + enumerable: true, + get: function() { + return this._curved; + }, + set: function(v) { + this._curved = !!v; + this._flagVertices = true; + } + }); + + Object.defineProperty(object, 'automatic', { + enumerable: true, + get: function() { + return this._automatic; + }, + set: function(v) { + if (v === this._automatic) { + return; + } + this._automatic = !!v; + var method = this._automatic ? 'ignore' : 'listen'; + _.each(this.vertices, function(v) { + v[method](); + }); + } + }); + + Object.defineProperty(object, 'beginning', { + enumerable: true, + get: function() { + return this._beginning; + }, + set: function(v) { + this._beginning = v; + this._flagVertices = true; + } + }); + + Object.defineProperty(object, 'ending', { + enumerable: true, + get: function() { + return this._ending; + }, + set: function(v) { + this._ending = v; + this._flagVertices = true; + } + }); + + Object.defineProperty(object, 'vertices', { + + enumerable: true, + + get: function() { + return this._collection; + }, + + set: function(vertices) { + + var updateVertices = this._renderer.flagVertices; + var bindVertices = this._renderer.bindVertices; + var unbindVertices = this._renderer.unbindVertices; + + // Remove previous listeners + if (this._collection) { + this._collection + .unbind(Two.Events.insert, bindVertices) + .unbind(Two.Events.remove, unbindVertices); + } + + // Create new Collection with copy of vertices + this._collection = new Two.Utils.Collection((vertices || []).slice(0)); + + // Listen for Collection changes and bind / unbind + this._collection + .bind(Two.Events.insert, bindVertices) + .bind(Two.Events.remove, unbindVertices); + + // Bind Initial Vertices + bindVertices(this._collection); + + } + + }); + + Object.defineProperty(object, 'clip', { + enumerable: true, + get: function() { + return this._clip; + }, + set: function(v) { + this._clip = v; + this._flagClip = true; + } + }); + + } + + }); + + _.extend(Path.prototype, Two.Shape.prototype, { + + // Flags + // http://en.wikipedia.org/wiki/Flag + + _flagVertices: true, + _flagLength: true, + + _flagFill: true, + _flagStroke: true, + _flagLinewidth: true, + _flagOpacity: true, + _flagVisible: true, + + _flagCap: true, + _flagJoin: true, + _flagMiter: true, + + _flagClip: false, + + // Underlying Properties + + _length: 0, + + _fill: '#fff', + _stroke: '#000', + _linewidth: 1.0, + _opacity: 1.0, + _visible: true, + + _cap: 'round', + _join: 'round', + _miter: 4, + + _closed: true, + _curved: false, + _automatic: true, + _beginning: 0, + _ending: 1.0, + + _clip: false, + + clone: function(parent) { + + parent = parent || this.parent; + + var points = _.map(this.vertices, function(v) { + return v.clone(); + }); + + var clone = new Path(points, this.closed, this.curved, !this.automatic); + + _.each(Two.Path.Properties, function(k) { + clone[k] = this[k]; + }, this); + + clone.translation.copy(this.translation); + clone.rotation = this.rotation; + clone.scale = this.scale; + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + toObject: function() { + + var result = { + vertices: _.map(this.vertices, function(v) { + return v.toObject(); + }) + }; + + _.each(Two.Shape.Properties, function(k) { + result[k] = this[k]; + }, this); + + result.translation = this.translation.toObject; + result.rotation = this.rotation; + result.scale = this.scale; + + return result; + + }, + + noFill: function() { + this.fill = 'transparent'; + return this; + }, + + noStroke: function() { + this.stroke = 'transparent'; + return this; + }, + + /** + * Orient the vertices of the shape to the upper lefthand + * corner of the path. + */ + corner: function() { + + var rect = this.getBoundingClientRect(true); + + rect.centroid = { + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2 + }; + + _.each(this.vertices, function(v) { + v.addSelf(rect.centroid); + }); + + return this; + + }, + + /** + * Orient the vertices of the shape to the center of the + * path. + */ + center: function() { + + var rect = this.getBoundingClientRect(true); + + rect.centroid = { + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2 + }; + + _.each(this.vertices, function(v) { + v.subSelf(rect.centroid); + }); + + // this.translation.addSelf(rect.centroid); + + return this; + + }, + + /** + * Remove self from the scene / parent. + */ + remove: function() { + + if (!this.parent) { + return this; + } + + this.parent.remove(this); + + return this; + + }, + + /** + * Return an object with top, left, right, bottom, width, and height + * parameters of the group. + */ + getBoundingClientRect: function(shallow) { + var matrix, border, l, x, y, i, v; + + var left = Infinity, right = -Infinity, + top = Infinity, bottom = -Infinity; + + // TODO: Update this to not __always__ update. Just when it needs to. + this._update(true); + + matrix = !!shallow ? this._matrix : getComputedMatrix(this); + + border = this.linewidth / 2; + l = this._vertices.length; + + if (l <= 0) { + v = matrix.multiply(0, 0, 1); + return { + top: v.y, + left: v.x, + right: v.x, + bottom: v.y, + width: 0, + height: 0 + }; + } + + for (i = 0; i < l; i++) { + v = this._vertices[i]; + + x = v.x; + y = v.y; + + v = matrix.multiply(x, y, 1); + top = min(v.y - border, top); + left = min(v.x - border, left); + right = max(v.x + border, right); + bottom = max(v.y + border, bottom); + } + + return { + top: top, + left: left, + right: right, + bottom: bottom, + width: right - left, + height: bottom - top + }; + + }, + + /** + * Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s + * coordinates to that percentage on this Two.Path's curve. + */ + getPointAt: function(t, obj) { + var ia, ib; + var x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right; + var target = this.length * Math.min(Math.max(t, 0), 1); + var length = this.vertices.length; + var last = length - 1; + + var a = null; + var b = null; + + for (var i = 0, l = this._lengths.length, sum = 0; i < l; i++) { + + if (sum + this._lengths[i] >= target) { + + if (this._closed) { + ia = Two.Utils.mod(i, length); + ib = Two.Utils.mod(i - 1, length); + if (i === 0) { + ia = ib; + ib = i; + } + } else { + ia = i; + ib = Math.min(Math.max(i - 1, 0), last); + } + + a = this.vertices[ia]; + b = this.vertices[ib]; + target -= sum; + if (this._lengths[i] !== 0) { + t = target / this._lengths[i]; + } + + break; + + } + + sum += this._lengths[i]; + + } + + // console.log(sum, a.command, b.command); + + if (_.isNull(a) || _.isNull(b)) { + return null; + } + + right = b.controls && b.controls.right; + left = a.controls && a.controls.left; + + x1 = b.x; + y1 = b.y; + x2 = (right || b).x; + y2 = (right || b).y; + x3 = (left || a).x; + y3 = (left || a).y; + x4 = a.x; + y4 = a.y; + + if (right && b._relative) { + x2 += b.x; + y2 += b.y; + } + + if (left && a._relative) { + x3 += a.x; + y3 += a.y; + } + + x = Two.Utils.getPointOnCubicBezier(t, x1, x2, x3, x4); + y = Two.Utils.getPointOnCubicBezier(t, y1, y2, y3, y4); + + if (_.isObject(obj)) { + obj.x = x; + obj.y = y; + return obj; + } + + return new Two.Vector(x, y); + + }, + + /** + * Based on closed / curved and sorting of vertices plot where all points + * should be and where the respective handles should be too. + */ + plot: function() { + + if (this.curved) { + Two.Utils.getCurveFromPoints(this._vertices, this.closed); + return this; + } + + for (var i = 0; i < this._vertices.length; i++) { + this._vertices[i]._command = i === 0 ? Two.Commands.move : Two.Commands.line; + } + + return this; + + }, + + subdivide: function(limit) { + //TODO: DRYness (function below) + this._update(); + + var last = this.vertices.length - 1; + var b = this.vertices[last]; + var closed = this._closed || this.vertices[last]._command === Two.Commands.close; + var points = []; + _.each(this.vertices, function(a, i) { + + if (i <= 0 && !closed) { + b = a; + return; + } + + if (a.command === Two.Commands.move) { + points.push(new Two.Anchor(b.x, b.y)); + if (i > 0) { + points[points.length - 1].command = Two.Commands.line; + } + b = a; + return; + } + + var verts = getSubdivisions(a, b, limit); + points = points.concat(verts); + + // Assign commands to all the verts + _.each(verts, function(v, i) { + if (i <= 0 && b.command === Two.Commands.move) { + v.command = Two.Commands.move; + } else { + v.command = Two.Commands.line; + } + }); + + if (i >= last) { + + // TODO: Add check if the two vectors in question are the same values. + if (this._closed && this._automatic) { + + b = a; + + verts = getSubdivisions(a, b, limit); + points = points.concat(verts); + + // Assign commands to all the verts + _.each(verts, function(v, i) { + if (i <= 0 && b.command === Two.Commands.move) { + v.command = Two.Commands.move; + } else { + v.command = Two.Commands.line; + } + }); + + } else if (closed) { + points.push(new Two.Anchor(a.x, a.y)); + } + + points[points.length - 1].command = closed ? Two.Commands.close : Two.Commands.line; + + } + + b = a; + + }, this); + + this._automatic = false; + this._curved = false; + this.vertices = points; + + return this; + + }, + + _updateLength: function(limit) { + //TODO: DRYness (function above) + this._update(); + + var length = this.vertices.length; + var last = length - 1; + var b = this.vertices[last]; + var closed = this._closed || this.vertices[last]._command === Two.Commands.close; + var sum = 0; + + if (_.isUndefined(this._lengths)) { + this._lengths = []; + } + + _.each(this.vertices, function(a, i) { + + if ((i <= 0 && !closed) || a.command === Two.Commands.move) { + b = a; + this._lengths[i] = 0; + return; + } + + this._lengths[i] = getCurveLength(a, b, limit); + sum += this._lengths[i]; + + if (i >= last && closed) { + + b = this.vertices[(i + 1) % length]; + + this._lengths[i + 1] = getCurveLength(a, b, limit); + sum += this._lengths[i + 1]; + + } + + b = a; + + }, this); + + this._length = sum; + + return this; + + }, + + _update: function() { + + if (this._flagVertices) { + + var l = this.vertices.length; + var last = l - 1, v; + + // TODO: Should clamp this so that `ia` and `ib` + // cannot select non-verts. + var ia = round((this._beginning) * last); + var ib = round((this._ending) * last); + + this._vertices.length = 0; + + for (var i = ia; i < ib + 1; i++) { + v = this.vertices[i]; + this._vertices.push(v); + } + + if (this._automatic) { + this.plot(); + } + + } + + Two.Shape.prototype._update.apply(this, arguments); + + return this; + + }, + + flagReset: function() { + + this._flagVertices = this._flagFill = this._flagStroke = + this._flagLinewidth = this._flagOpacity = this._flagVisible = + this._flagCap = this._flagJoin = this._flagMiter = + this._flagClip = false; + + Two.Shape.prototype.flagReset.call(this); + + return this; + + } + + }); + + Path.MakeObservable(Path.prototype); + + /** + * Utility functions + */ + + function getCurveLength(a, b, limit) { + // TODO: DRYness + var x1, x2, x3, x4, y1, y2, y3, y4; + + var right = b.controls && b.controls.right; + var left = a.controls && a.controls.left; + + x1 = b.x; + y1 = b.y; + x2 = (right || b).x; + y2 = (right || b).y; + x3 = (left || a).x; + y3 = (left || a).y; + x4 = a.x; + y4 = a.y; + + if (right && b._relative) { + x2 += b.x; + y2 += b.y; + } + + if (left && a._relative) { + x3 += a.x; + y3 += a.y; + } + + return Two.Utils.getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit); + + } + + function getSubdivisions(a, b, limit) { + // TODO: DRYness + var x1, x2, x3, x4, y1, y2, y3, y4; + + var right = b.controls && b.controls.right; + var left = a.controls && a.controls.left; + + x1 = b.x; + y1 = b.y; + x2 = (right || b).x; + y2 = (right || b).y; + x3 = (left || a).x; + y3 = (left || a).y; + x4 = a.x; + y4 = a.y; + + if (right && b._relative) { + x2 += b.x; + y2 += b.y; + } + + if (left && a._relative) { + x3 += a.x; + y3 += a.y; + } + + return Two.Utils.subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit); + + } + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path; + var _ = Two.Utils; + + var Line = Two.Line = function(x1, y1, x2, y2) { + + var width = x2 - x1; + var height = y2 - y1; + + var w2 = width / 2; + var h2 = height / 2; + + Path.call(this, [ + new Two.Anchor(- w2, - h2), + new Two.Anchor(w2, h2) + ]); + + this.translation.set(x1 + w2, y1 + h2); + + }; + + _.extend(Line.prototype, Path.prototype); + + Path.MakeObservable(Line.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path; + var _ = Two.Utils; + + var Rectangle = Two.Rectangle = function(x, y, width, height) { + + Path.call(this, [ + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor() + ], true); + + this.width = width; + this.height = height; + this._update(); + + this.translation.set(x, y); + + }; + + _.extend(Rectangle, { + + Properties: ['width', 'height'], + + MakeObservable: function(obj) { + Path.MakeObservable(obj); + _.each(Rectangle.Properties, Two.Utils.defineProperty, obj); + } + + }); + + _.extend(Rectangle.prototype, Path.prototype, { + + _width: 0, + _height: 0, + + _flagWidth: 0, + _flagHeight: 0, + + _update: function() { + + if (this._flagWidth || this._flagHeight) { + + var xr = this._width / 2; + var yr = this._height / 2; + + this.vertices[0].set(-xr, -yr); + this.vertices[1].set(xr, -yr); + this.vertices[2].set(xr, yr); + this.vertices[3].set(-xr, yr); + + } + + Path.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + this._flagWidth = this._flagHeight = false; + Path.prototype.flagReset.call(this); + + return this; + + } + + }); + + Rectangle.MakeObservable(Rectangle.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; + var _ = Two.Utils; + + var Ellipse = Two.Ellipse = function(ox, oy, rx, ry) { + + if (!_.isNumber(ry)) { + ry = rx; + } + + var amount = Two.Resolution; + + var points = _.map(_.range(amount), function(i) { + return new Two.Anchor(); + }, this); + + Path.call(this, points, true, true); + + this.width = rx * 2; + this.height = ry * 2; + + this._update(); + this.translation.set(ox, oy); + + }; + + _.extend(Ellipse, { + + Properties: ['width', 'height'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(Ellipse.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(Ellipse.prototype, Path.prototype, { + + _width: 0, + _height: 0, + + _flagWidth: false, + _flagHeight: false, + + _update: function() { + + if (this._flagWidth || this._flagHeight) { + for (var i = 0, l = this.vertices.length; i < l; i++) { + var pct = i / l; + var theta = pct * TWO_PI; + var x = this._width * cos(theta) / 2; + var y = this._height * sin(theta) / 2; + this.vertices[i].set(x, y); + } + } + + Path.prototype._update.call(this); + return this; + + }, + + flagReset: function() { + + this._flagWidth = this._flagHeight = false; + + Path.prototype.flagReset.call(this); + return this; + + } + + }); + + Ellipse.MakeObservable(Ellipse.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; + var _ = Two.Utils; + + var Circle = Two.Circle = function(ox, oy, r) { + + var amount = Two.Resolution; + + var points = _.map(_.range(amount), function(i) { + return new Two.Anchor(); + }, this); + + Path.call(this, points, true, true); + + this.radius = r; + + this._update(); + this.translation.set(ox, oy); + + }; + + _.extend(Circle, { + + Properties: ['radius'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(Circle.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(Circle.prototype, Path.prototype, { + + _radius: 0, + _flagRadius: false, + + _update: function() { + + if (this._flagRadius) { + for (var i = 0, l = this.vertices.length; i < l; i++) { + var pct = i / l; + var theta = pct * TWO_PI; + var x = this._radius * cos(theta); + var y = this._radius * sin(theta); + this.vertices[i].set(x, y); + } + } + + Path.prototype._update.call(this); + return this; + + }, + + flagReset: function() { + + this._flagRadius = false; + + Path.prototype.flagReset.call(this); + return this; + + } + + }); + + Circle.MakeObservable(Circle.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; + var _ = Two.Utils; + + var Polygon = Two.Polygon = function(ox, oy, r, sides) { + + sides = Math.max(sides || 0, 3); + + var points = _.map(_.range(sides), function(i) { + return new Two.Anchor(); + }); + + Path.call(this, points, true); + + this.width = r * 2; + this.height = r * 2; + this.sides = sides; + + this._update(); + this.translation.set(ox, oy); + + }; + + _.extend(Polygon, { + + Properties: ['width', 'height', 'sides'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(Polygon.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(Polygon.prototype, Path.prototype, { + + _width: 0, + _height: 0, + _sides: 0, + + _flagWidth: false, + _flagHeight: false, + _flagSides: false, + + _update: function() { + + if (this._flagWidth || this._flagHeight || this._flagSides) { + + var sides = this._sides; + var amount = this.vertices.length; + + if (amount > sides) { + this.vertices.splice(sides - 1, amount - sides); + } + + for (var i = 0; i < sides; i++) { + + var pct = (i + 0.5) / sides; + var theta = TWO_PI * pct + Math.PI / 2; + var x = this._width * cos(theta); + var y = this._height * sin(theta); + + if (i >= amount) { + this.vertices.push(new Two.Anchor(x, y)); + } else { + this.vertices[i].set(x, y); + } + + } + + } + + Path.prototype._update.call(this); + return this; + + }, + + flagReset: function() { + + this._flagWidth = this._flagHeight = this._flagSides = false; + Path.prototype.flagReset.call(this); + + return this; + + } + + }); + + Polygon.MakeObservable(Polygon.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path, PI = Math.PI, TWO_PI = Math.PI * 2, HALF_PI = Math.PI / 2, + cos = Math.cos, sin = Math.sin, abs = Math.abs, _ = Two.Utils; + + var ArcSegment = Two.ArcSegment = function(ox, oy, ir, or, sa, ea, res) { + + var points = _.map(_.range(res || (Two.Resolution * 3)), function() { + return new Two.Anchor(); + }); + + Path.call(this, points, false, false, true); + + this.innerRadius = ir; + this.outerRadius = or; + + this.startAngle = sa; + this.endAngle = ea; + + this._update(); + this.translation.set(ox, oy); + + } + + _.extend(ArcSegment, { + + Properties: ['startAngle', 'endAngle', 'innerRadius', 'outerRadius'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(ArcSegment.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(ArcSegment.prototype, Path.prototype, { + + _flagStartAngle: false, + _flagEndAngle: false, + _flagInnerRadius: false, + _flagOuterRadius: false, + + _startAngle: 0, + _endAngle: TWO_PI, + _innerRadius: 0, + _outerRadius: 0, + + _update: function() { + + if (this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius + || this._flagOuterRadius) { + + var sa = this._startAngle; + var ea = this._endAngle; + + var ir = this._innerRadius; + var or = this._outerRadius; + + var connected = mod(sa, TWO_PI) === mod(ea, TWO_PI); + var punctured = ir > 0; + + var vertices = this.vertices; + var length = (punctured ? vertices.length / 2 : vertices.length); + var command, id = 0; + + if (connected) { + length--; + } else if (!punctured) { + length -= 2; + } + + /** + * Outer Circle + */ + for (var i = 0, last = length - 1; i < length; i++) { + + var pct = i / last; + var v = vertices[id]; + var theta = pct * (ea - sa) + sa; + var step = (ea - sa) / length; + + var x = or * Math.cos(theta); + var y = or * Math.sin(theta); + + switch (i) { + case 0: + command = Two.Commands.move; + break; + default: + command = Two.Commands.curve; + } + + v.command = command; + v.x = x; + v.y = y; + v.controls.left.clear(); + v.controls.right.clear(); + + if (v.command === Two.Commands.curve) { + var amp = or * step / Math.PI; + v.controls.left.x = amp * Math.cos(theta - HALF_PI); + v.controls.left.y = amp * Math.sin(theta - HALF_PI); + v.controls.right.x = amp * Math.cos(theta + HALF_PI); + v.controls.right.y = amp * Math.sin(theta + HALF_PI); + if (i === 1) { + v.controls.left.multiplyScalar(2); + } + if (i === last) { + v.controls.right.multiplyScalar(2); + } + } + + id++; + + } + + if (punctured) { + + if (connected) { + vertices[id].command = Two.Commands.close; + id++; + } else { + length--; + last = length - 1; + } + + /** + * Inner Circle + */ + for (i = 0; i < length; i++) { + + pct = i / last; + v = vertices[id]; + theta = (1 - pct) * (ea - sa) + sa; + step = (ea - sa) / length; + + x = ir * Math.cos(theta); + y = ir * Math.sin(theta); + command = Two.Commands.curve; + if (i <= 0) { + command = connected ? Two.Commands.move : Two.Commands.line; + } + + v.command = command; + v.x = x; + v.y = y; + v.controls.left.clear(); + v.controls.right.clear(); + + if (v.command === Two.Commands.curve) { + amp = ir * step / Math.PI; + v.controls.left.x = amp * Math.cos(theta + HALF_PI); + v.controls.left.y = amp * Math.sin(theta + HALF_PI); + v.controls.right.x = amp * Math.cos(theta - HALF_PI); + v.controls.right.y = amp * Math.sin(theta - HALF_PI); + if (i === 1) { + v.controls.left.multiplyScalar(2); + } + if (i === last) { + v.controls.right.multiplyScalar(2); + } + } + + id++; + + } + + } else if (!connected) { + + vertices[id].command = Two.Commands.line; + vertices[id].x = 0; + vertices[id].y = 0; + id++; + + } + + /** + * Final Point + */ + vertices[id].command = Two.Commands.close; + + } + + Path.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + Path.prototype.flagReset.call(this); + + this._flagStartAngle = this._flagEndAngle + = this._flagInnerRadius = this._flagOuterRadius = false; + + return this; + + } + + }); + + ArcSegment.MakeObservable(ArcSegment.prototype); + + function mod(v, l) { + while (v < 0) { + v += l; + } + return v % l; + } + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; + var _ = Two.Utils; + + var Star = Two.Star = function(ox, oy, or, ir, sides) { + + if (!_.isNumber(ir)) { + ir = or / 2; + } + + if (!_.isNumber(sides) || sides <= 0) { + sides = 5; + } + + var length = sides * 2; + + var points = _.map(_.range(length), function(i) { + return new Two.Anchor(); + }); + + Path.call(this, points, true); + + this.innerRadius = ir; + this.outerRadius = or; + this.sides = sides; + + this._update(); + this.translation.set(ox, oy); + + }; + + _.extend(Star, { + + Properties: ['innerRadius', 'outerRadius', 'sides'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(Star.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(Star.prototype, Path.prototype, { + + _innerRadius: 0, + _outerRadius: 0, + _sides: 0, + + _flagInnerRadius: false, + _flagOuterRadius: false, + _flagSides: false, + + _update: function() { + + if (this._flagInnerRadius || this._flagOuterRadius || this._flagSides) { + + var sides = this._sides * 2; + var amount = this.vertices.length; + + if (amount > sides) { + this.vertices.splice(sides - 1, amount - sides); + } + + for (var i = 0; i < sides; i++) { + + var pct = (i + 0.5) / sides; + var theta = TWO_PI * pct; + var r = (i % 2 ? this._innerRadius : this._outerRadius); + var x = r * cos(theta); + var y = r * sin(theta); + + if (i >= amount) { + this.vertices.push(new Two.Anchor(x, y)); + } else { + this.vertices[i].set(x, y); + } + + } + + } + + Path.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false; + Path.prototype.flagReset.call(this); + + return this; + + } + + }); + + Star.MakeObservable(Star.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var Path = Two.Path; + var _ = Two.Utils; + + var RoundedRectangle = Two.RoundedRectangle = function(ox, oy, width, height, radius) { + + if (!_.isNumber(radius)) { + radius = Math.floor(Math.min(width, height) / 12); + } + + var amount = 10; + + var points = _.map(_.range(amount), function(i) { + return new Two.Anchor(0, 0, 0, 0, 0, 0, + i === 0 ? Two.Commands.move : Two.Commands.curve); + }); + + points[points.length - 1].command = Two.Commands.close; + + Path.call(this, points, false, false, true); + + this.width = width; + this.height = height; + this.radius = radius; + + this._update(); + this.translation.set(ox, oy); + + }; + + _.extend(RoundedRectangle, { + + Properties: ['width', 'height', 'radius'], + + MakeObservable: function(obj) { + + Path.MakeObservable(obj); + _.each(RoundedRectangle.Properties, Two.Utils.defineProperty, obj); + + } + + }); + + _.extend(RoundedRectangle.prototype, Path.prototype, { + + _width: 0, + _height: 0, + _radius: 0, + + _flagWidth: false, + _flagHeight: false, + _flagRadius: false, + + _update: function() { + + if (this._flagWidth || this._flagHeight || this._flagRadius) { + + var width = this._width; + var height = this._height; + var radius = Math.min(Math.max(this._radius, 0), + Math.min(width, height)); + + var v; + var w = width / 2; + var h = height / 2; + + v = this.vertices[0]; + v.x = - (w - radius); + v.y = - h; + + // Upper Right Corner + + v = this.vertices[1]; + v.x = (w - radius); + v.y = - h; + v.controls.left.clear(); + v.controls.right.x = radius; + v.controls.right.y = 0; + + v = this.vertices[2]; + v.x = w; + v.y = - (h - radius); + v.controls.right.clear(); + v.controls.left.clear(); + + // Bottom Right Corner + + v = this.vertices[3]; + v.x = w; + v.y = (h - radius); + v.controls.left.clear(); + v.controls.right.x = 0; + v.controls.right.y = radius; + + v = this.vertices[4]; + v.x = (w - radius); + v.y = h; + v.controls.right.clear(); + v.controls.left.clear(); + + // Bottom Left Corner + + v = this.vertices[5]; + v.x = - (w - radius); + v.y = h; + v.controls.left.clear(); + v.controls.right.x = - radius; + v.controls.right.y = 0; + + v = this.vertices[6]; + v.x = - w; + v.y = (h - radius); + v.controls.left.clear(); + v.controls.right.clear(); + + // Upper Left Corner + + v = this.vertices[7]; + v.x = - w; + v.y = - (h - radius); + v.controls.left.clear(); + v.controls.right.x = 0; + v.controls.right.y = - radius; + + v = this.vertices[8]; + v.x = - (w - radius); + v.y = - h; + v.controls.left.clear(); + v.controls.right.clear(); + + v = this.vertices[9]; + v.copy(this.vertices[8]); + + } + + Path.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + this._flagWidth = this._flagHeight = this._flagRadius = false; + Path.prototype.flagReset.call(this); + + return this; + + } + + }); + + RoundedRectangle.MakeObservable(RoundedRectangle.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var root = Two.root; + var getComputedMatrix = Two.Utils.getComputedMatrix; + var _ = Two.Utils; + + var canvas = (root.document ? root.document.createElement('canvas') : { getContext: _.identity }); + var ctx = canvas.getContext('2d'); + + var Text = Two.Text = function(message, x, y, styles) { + + Two.Shape.call(this); + + this._renderer.type = 'text'; + this._renderer.flagFill = _.bind(Text.FlagFill, this); + this._renderer.flagStroke = _.bind(Text.FlagStroke, this); + + this.value = message; + + if (_.isNumber(x)) { + this.translation.x = x; + } + if (_.isNumber(y)) { + this.translation.y = y; + } + + if (!_.isObject(styles)) { + return this; + } + + _.each(Two.Text.Properties, function(property) { + + if (property in styles) { + this[property] = styles[property]; + } + + }, this); + + }; + + _.extend(Two.Text, { + + Properties: [ + 'value', 'family', 'size', 'leading', 'alignment', 'linewidth', 'style', + 'weight', 'decoration', 'baseline', 'opacity', 'visible', 'fill', 'stroke' + ], + + FlagFill: function() { + this._flagFill = true; + }, + + FlagStroke: function() { + this._flagStroke = true; + }, + + MakeObservable: function(object) { + + Two.Shape.MakeObservable(object); + + _.each(Two.Text.Properties.slice(0, 12), Two.Utils.defineProperty, object); + + Object.defineProperty(object, 'fill', { + enumerable: true, + get: function() { + return this._fill; + }, + set: function(f) { + + if (this._fill instanceof Two.Gradient + || this._fill instanceof Two.LinearGradient + || this._fill instanceof Two.RadialGradient + || this._fill instanceof Two.Texture) { + this._fill.unbind(Two.Events.change, this._renderer.flagFill); + } + + this._fill = f; + this._flagFill = true; + + if (this._fill instanceof Two.Gradient + || this._fill instanceof Two.LinearGradient + || this._fill instanceof Two.RadialGradient + || this._fill instanceof Two.Texture) { + this._fill.bind(Two.Events.change, this._renderer.flagFill); + } + + } + }); + + Object.defineProperty(object, 'stroke', { + enumerable: true, + get: function() { + return this._stroke; + }, + set: function(f) { + + if (this._stroke instanceof Two.Gradient + || this._stroke instanceof Two.LinearGradient + || this._stroke instanceof Two.RadialGradient + || this._stroke instanceof Two.Texture) { + this._stroke.unbind(Two.Events.change, this._renderer.flagStroke); + } + + this._stroke = f; + this._flagStroke = true; + + if (this._stroke instanceof Two.Gradient + || this._stroke instanceof Two.LinearGradient + || this._stroke instanceof Two.RadialGradient + || this._stroke instanceof Two.Texture) { + this._stroke.bind(Two.Events.change, this._renderer.flagStroke); + } + + } + }); + + Object.defineProperty(object, 'clip', { + enumerable: true, + get: function() { + return this._clip; + }, + set: function(v) { + this._clip = v; + this._flagClip = true; + } + }); + + } + + }); + + _.extend(Two.Text.prototype, Two.Shape.prototype, { + + // Flags + // http://en.wikipedia.org/wiki/Flag + + _flagValue: true, + _flagFamily: true, + _flagSize: true, + _flagLeading: true, + _flagAlignment: true, + _flagBaseline: true, + _flagStyle: true, + _flagWeight: true, + _flagDecoration: true, + + _flagFill: true, + _flagStroke: true, + _flagLinewidth: true, + _flagOpacity: true, + _flagVisible: true, + + _flagClip: false, + + // Underlying Properties + + _value: '', + _family: 'sans-serif', + _size: 13, + _leading: 17, + _alignment: 'center', + _baseline: 'middle', + _style: 'normal', + _weight: 500, + _decoration: 'none', + + _fill: '#000', + _stroke: 'transparent', + _linewidth: 1, + _opacity: 1, + _visible: true, + + _clip: false, + + remove: function() { + + if (!this.parent) { + return this; + } + + this.parent.remove(this); + + return this; + + }, + + clone: function(parent) { + + var parent = parent || this.parent; + + var clone = new Two.Text(this.value); + clone.translation.copy(this.translation); + clone.rotation = this.rotation; + clone.scale = this.scale; + + _.each(Two.Text.Properties, function(property) { + clone[property] = this[property]; + }, this); + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + toObject: function() { + + var result = { + translation: this.translation.toObject(), + rotation: this.rotation, + scale: this.scale + }; + + _.each(Two.Text.Properties, function(property) { + result[property] = this[property]; + }, this); + + return result; + + }, + + noStroke: function() { + this.stroke = 'transparent'; + return this; + }, + + noFill: function() { + this.fill = 'transparent'; + return this; + }, + + /** + * A shim to not break `getBoundingClientRect` calls. TODO: Implement a + * way to calculate proper bounding boxes of `Two.Text`. + */ + getBoundingClientRect: function(shallow) { + + var matrix, border, l, x, y, i, v; + + var left = Infinity, right = -Infinity, + top = Infinity, bottom = -Infinity; + + // TODO: Update this to not __always__ update. Just when it needs to. + this._update(true); + + matrix = !!shallow ? this._matrix : getComputedMatrix(this); + + v = matrix.multiply(0, 0, 1); + + return { + top: v.x, + left: v.y, + right: v.x, + bottom: v.y, + width: 0, + height: 0 + }; + + }, + + flagReset: function() { + + this._flagValue = this._flagFamily = this._flagSize = + this._flagLeading = this._flagAlignment = this._flagFill = + this._flagStroke = this._flagLinewidth = this._flagOpaicty = + this._flagVisible = this._flagClip = this._flagDecoration = + this._flagBaseline = false; + + Two.Shape.prototype.flagReset.call(this); + + return this; + + } + + }); + + Two.Text.MakeObservable(Two.Text.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var Stop = Two.Stop = function(offset, color, opacity) { + + this._renderer = {}; + this._renderer.type = 'stop'; + + this.offset = _.isNumber(offset) ? offset + : Stop.Index <= 0 ? 0 : 1; + + this.opacity = _.isNumber(opacity) ? opacity : 1; + + this.color = _.isString(color) ? color + : Stop.Index <= 0 ? '#fff' : '#000'; + + Stop.Index = (Stop.Index + 1) % 2; + + }; + + _.extend(Stop, { + + Index: 0, + + Properties: [ + 'offset', + 'opacity', + 'color' + ], + + MakeObservable: function(object) { + + _.each(Stop.Properties, function(property) { + + var object = this; + var secret = '_' + property; + var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1); + + Object.defineProperty(object, property, { + enumerable: true, + get: function() { + return this[secret]; + }, + set: function(v) { + this[secret] = v; + this[flag] = true; + if (this.parent) { + this.parent._flagStops = true; + } + } + }); + + }, object); + + } + + }); + + _.extend(Stop.prototype, Two.Utils.Events, { + + clone: function() { + + var clone = new Stop(); + + _.each(Stop.Properties, function(property) { + clone[property] = this[property]; + }, this); + + return clone; + + }, + + toObject: function() { + + var result = {}; + + _.each(Stop.Properties, function(k) { + result[k] = this[k]; + }, this); + + return result; + + }, + + flagReset: function() { + + this._flagOffset = this._flagColor = this._flagOpacity = false; + + return this; + + } + + }); + + Stop.MakeObservable(Stop.prototype); + + var Gradient = Two.Gradient = function(stops) { + + this._renderer = {}; + this._renderer.type = 'gradient'; + + this.id = Two.Identifier + Two.uniqueId(); + this.classList = []; + + this._renderer.flagStops = _.bind(Gradient.FlagStops, this); + this._renderer.bindStops = _.bind(Gradient.BindStops, this); + this._renderer.unbindStops = _.bind(Gradient.UnbindStops, this); + + this.spread = 'pad'; + + this.stops = stops; + + }; + + _.extend(Gradient, { + + Stop: Stop, + + Properties: [ + 'spread' + ], + + MakeObservable: function(object) { + + _.each(Gradient.Properties, Two.Utils.defineProperty, object); + + Object.defineProperty(object, 'stops', { + + enumerable: true, + + get: function() { + return this._stops; + }, + + set: function(stops) { + + var updateStops = this._renderer.flagStops; + var bindStops = this._renderer.bindStops; + var unbindStops = this._renderer.unbindStops; + + // Remove previous listeners + if (this._stops) { + this._stops + .unbind(Two.Events.insert, bindStops) + .unbind(Two.Events.remove, unbindStops); + } + + // Create new Collection with copy of Stops + this._stops = new Two.Utils.Collection((stops || []).slice(0)); + + // Listen for Collection changes and bind / unbind + this._stops + .bind(Two.Events.insert, bindStops) + .bind(Two.Events.remove, unbindStops); + + // Bind Initial Stops + bindStops(this._stops); + + } + + }); + + }, + + FlagStops: function() { + this._flagStops = true; + }, + + BindStops: function(items) { + + // This function is called a lot + // when importing a large SVG + var i = items.length; + while(i--) { + items[i].bind(Two.Events.change, this._renderer.flagStops); + items[i].parent = this; + } + + this._renderer.flagStops(); + + }, + + UnbindStops: function(items) { + + var i = items.length; + while(i--) { + items[i].unbind(Two.Events.change, this._renderer.flagStops); + delete items[i].parent; + } + + this._renderer.flagStops(); + + } + + }); + + _.extend(Gradient.prototype, Two.Utils.Events, { + + _flagStops: false, + _flagSpread: false, + + clone: function(parent) { + + parent = parent || this.parent; + + var stops = _.map(this.stops, function(s) { + return s.clone(); + }); + + var clone = new Gradient(stops); + + _.each(Two.Gradient.Properties, function(k) { + clone[k] = this[k]; + }, this); + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + toObject: function() { + + var result = { + stops: _.map(this.stops, function(s) { + return s.toObject(); + }) + }; + + _.each(Gradient.Properties, function(k) { + result[k] = this[k]; + }, this); + + return result; + + }, + + _update: function() { + + if (this._flagSpread || this._flagStops) { + this.trigger(Two.Events.change); + } + + return this; + + }, + + flagReset: function() { + + this._flagSpread = this._flagStops = false; + + return this; + + } + + }); + + Gradient.MakeObservable(Gradient.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var LinearGradient = Two.LinearGradient = function(x1, y1, x2, y2, stops) { + + Two.Gradient.call(this, stops); + + this._renderer.type = 'linear-gradient'; + + var flagEndPoints = _.bind(LinearGradient.FlagEndPoints, this); + this.left = new Two.Vector().bind(Two.Events.change, flagEndPoints); + this.right = new Two.Vector().bind(Two.Events.change, flagEndPoints); + + if (_.isNumber(x1)) { + this.left.x = x1; + } + if (_.isNumber(y1)) { + this.left.y = y1; + } + if (_.isNumber(x2)) { + this.right.x = x2; + } + if (_.isNumber(y2)) { + this.right.y = y2; + } + + }; + + _.extend(LinearGradient, { + + Stop: Two.Gradient.Stop, + + MakeObservable: function(object) { + Two.Gradient.MakeObservable(object); + }, + + FlagEndPoints: function() { + this._flagEndPoints = true; + } + + }); + + _.extend(LinearGradient.prototype, Two.Gradient.prototype, { + + _flagEndPoints: false, + + clone: function(parent) { + + parent = parent || this.parent; + + var stops = _.map(this.stops, function(stop) { + return stop.clone(); + }); + + var clone = new LinearGradient(this.left._x, this.left._y, + this.right._x, this.right._y, stops); + + _.each(Two.Gradient.Properties, function(k) { + clone[k] = this[k]; + }, this); + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + toObject: function() { + + var result = Two.Gradient.prototype.toObject.call(this); + + result.left = this.left.toObject(); + result.right = this.right.toObject(); + + return result; + + }, + + _update: function() { + + if (this._flagEndPoints || this._flagSpread || this._flagStops) { + this.trigger(Two.Events.change); + } + + return this; + + }, + + flagReset: function() { + + this._flagEndPoints = false; + + Two.Gradient.prototype.flagReset.call(this); + + return this; + + } + + }); + + LinearGradient.MakeObservable(LinearGradient.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + + var RadialGradient = Two.RadialGradient = function(cx, cy, r, stops, fx, fy) { + + Two.Gradient.call(this, stops); + + this._renderer.type = 'radial-gradient'; + + this.center = new Two.Vector() + .bind(Two.Events.change, _.bind(function() { + this._flagCenter = true; + }, this)); + + this.radius = _.isNumber(r) ? r : 20; + + this.focal = new Two.Vector() + .bind(Two.Events.change, _.bind(function() { + this._flagFocal = true; + }, this)); + + if (_.isNumber(cx)) { + this.center.x = cx; + } + if (_.isNumber(cy)) { + this.center.y = cy; + } + + this.focal.copy(this.center); + + if (_.isNumber(fx)) { + this.focal.x = fx; + } + if (_.isNumber(fy)) { + this.focal.y = fy; + } + + }; + + _.extend(RadialGradient, { + + Stop: Two.Gradient.Stop, + + Properties: [ + 'radius' + ], + + MakeObservable: function(object) { + + Two.Gradient.MakeObservable(object); + + _.each(RadialGradient.Properties, Two.Utils.defineProperty, object); + + } + + }); + + _.extend(RadialGradient.prototype, Two.Gradient.prototype, { + + _flagRadius: false, + _flagCenter: false, + _flagFocal: false, + + clone: function(parent) { + + parent = parent || this.parent; + + var stops = _.map(this.stops, function(stop) { + return stop.clone(); + }); + + var clone = new RadialGradient(this.center._x, this.center._y, + this._radius, stops, this.focal._x, this.focal._y); + + _.each(Two.Gradient.Properties.concat(RadialGradient.Properties), function(k) { + clone[k] = this[k]; + }, this); + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + toObject: function() { + + var result = Two.Gradient.prototype.toObject.call(this); + + _.each(RadialGradient.Properties, function(k) { + result[k] = this[k]; + }, this); + + result.center = this.center.toObject(); + result.focal = this.focal.toObject(); + + return result; + + }, + + _update: function() { + + if (this._flagRadius || this._flatCenter || this._flagFocal + || this._flagSpread || this._flagStops) { + this.trigger(Two.Events.change); + } + + return this; + + }, + + flagReset: function() { + + this._flagRadius = this._flagCenter = this._flagFocal = false; + + Two.Gradient.prototype.flagReset.call(this); + + return this; + + } + + }); + + RadialGradient.MakeObservable(RadialGradient.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + var anchor; + var regex = { + video: /\.(mp4|webm)$/i, + image: /\.(jpe?g|png|gif|tiff)$/i + }; + + if (this.document) { + anchor = document.createElement('a'); + } + + var Texture = Two.Texture = function(src, callback) { + + this._renderer = {}; + this._renderer.type = 'texture'; + this._renderer.flagOffset = _.bind(Texture.FlagOffset, this); + this._renderer.flagScale = _.bind(Texture.FlagScale, this); + + this.id = Two.Identifier + Two.uniqueId(); + this.classList = []; + + this.offset = new Two.Vector(); + + if (_.isFunction(callback)) { + var loaded = _.bind(function() { + this.unbind(Two.Events.load, loaded); + if (_.isFunction(callback)) { + callback(); + } + }, this); + this.bind(Two.Events.load, loaded); + } + + if (_.isString(src)) { + this.src = src; + } else if (_.isElement(src)) { + this.image = src; + } + + this._update(); + + }; + + _.extend(Texture, { + + Properties: [ + 'src', + 'loaded', + 'repeat' + ], + + ImageRegistry: new Two.Registry(), + + getAbsoluteURL: function(path) { + if (!anchor) { + // TODO: Fix for headless environment + return path; + } + anchor.href = path; + return anchor.href; + }, + + getImage: function(src) { + + var absoluteSrc = Texture.getAbsoluteURL(src); + + if (Texture.ImageRegistry.contains(absoluteSrc)) { + return Texture.ImageRegistry.get(absoluteSrc); + } + + var image; + + if (regex.video.test(absoluteSrc)) { + image = document.createElement('video'); + } else { + image = document.createElement('img'); + } + + image.crossOrigin = 'anonymous'; + + return image; + + }, + + Register: { + canvas: function(texture, callback) { + texture._src = '#' + texture.id; + Texture.ImageRegistry.add(texture.src, texture.image); + if (_.isFunction(callback)) { + callback(); + } + }, + img: function(texture, callback) { + + var loaded = function(e) { + texture.image.removeEventListener('load', loaded, false); + texture.image.removeEventListener('error', error, false); + if (_.isFunction(callback)) { + callback(); + } + }; + var error = function(e) { + texture.image.removeEventListener('load', loaded, false); + texture.image.removeEventListener('error', error, false); + throw new Two.Utils.Error('unable to load ' + texture.src); + }; + + if (_.isNumber(texture.image.width) && texture.image.width > 0 + && _.isNumber(texture.image.height) && texture.image.height > 0) { + loaded(); + } else { + texture.image.addEventListener('load', loaded, false); + texture.image.addEventListener('error', error, false); + } + + texture._src = Texture.getAbsoluteURL(texture._src); + + if (texture.image && texture.image.getAttribute('two-src')) { + return; + } + + texture.image.setAttribute('two-src', texture.src); + Texture.ImageRegistry.add(texture.src, texture.image); + texture.image.src = texture.src; + + }, + video: function(texture, callback) { + + var loaded = function(e) { + texture.image.removeEventListener('load', loaded, false); + texture.image.removeEventListener('error', error, false); + texture.image.width = texture.image.videoWidth; + texture.image.height = texture.image.videoHeight; + texture.image.play(); + if (_.isFunction(callback)) { + callback(); + } + }; + var error = function(e) { + texture.image.removeEventListener('load', loaded, false); + texture.image.removeEventListener('error', error, false); + throw new Two.Utils.Error('unable to load ' + texture.src); + }; + + texture._src = Texture.getAbsoluteURL(texture._src); + texture.image.addEventListener('canplaythrough', loaded, false); + texture.image.addEventListener('error', error, false); + + if (texture.image && texture.image.getAttribute('two-src')) { + return; + } + + texture.image.setAttribute('two-src', texture.src); + Texture.ImageRegistry.add(texture.src, texture.image); + texture.image.src = texture.src; + texture.image.loop = true; + texture.image.load(); + + } + }, + + load: function(texture, callback) { + + var src = texture.src; + var image = texture.image; + var tag = image && image.nodeName.toLowerCase(); + + if (texture._flagImage) { + if (/canvas/i.test(tag)) { + Texture.Register.canvas(texture, callback); + } else { + texture._src = image.getAttribute('two-src') || image.src; + Texture.Register[tag](texture, callback); + } + } + + if (texture._flagSrc) { + if (!image) { + texture.image = Texture.getImage(texture.src); + } + tag = texture.image.nodeName.toLowerCase(); + Texture.Register[tag](texture, callback); + } + + }, + + FlagOffset: function() { + this._flagOffset = true; + }, + + FlagScale: function() { + this._flagScale = true; + }, + + MakeObservable: function(object) { + + _.each(Texture.Properties, Two.Utils.defineProperty, object); + + Object.defineProperty(object, 'image', { + enumerable: true, + get: function() { + return this._image; + }, + set: function(image) { + + var tag = image && image.nodeName.toLowerCase(); + var index; + + switch (tag) { + case 'canvas': + index = '#' + image.id; + break; + default: + index = image.src; + } + + if (Texture.ImageRegistry.contains(index)) { + this._image = Texture.ImageRegistry.get(image.src); + } else { + this._image = image; + } + + this._flagImage = true; + + } + + }); + + Object.defineProperty(object, 'offset', { + enumerable: true, + get: function() { + return this._offset; + }, + set: function(v) { + if (this._offset) { + this._offset.unbind(Two.Events.change, this._renderer.flagOffset); + } + this._offset = v; + this._offset.bind(Two.Events.change, this._renderer.flagOffset); + this._flagOffset = true; + } + }); + + Object.defineProperty(object, 'scale', { + enumerable: true, + get: function() { + return this._scale; + }, + set: function(v) { + + if (this._scale instanceof Two.Vector) { + this._scale.unbind(Two.Events.change, this._renderer.flagScale); + } + + this._scale = v; + + if (this._scale instanceof Two.Vector) { + this._scale.bind(Two.Events.change, this._renderer.flagScale); + } + + this._flagScale = true; + + } + }); + + } + + }); + + _.extend(Texture.prototype, Two.Utils.Events, Two.Shape.prototype, { + + _flagSrc: false, + _flagImage: false, + _flagVideo: false, + _flagLoaded: false, + _flagRepeat: false, + + _flagOffset: false, + _flagScale: false, + + _src: '', + _image: null, + _loaded: false, + _repeat: 'no-repeat', + + _scale: 1, + _offset: null, + + clone: function() { + return new Texture(this.src); + }, + + toObject: function() { + return { + src: this.src, + image: this.image + } + }, + + _update: function() { + + if (this._flagSrc || this._flagImage || this._flagVideo) { + + this.trigger(Two.Events.change); + + if (this._flagSrc || this._flagImage) { + this.loaded = false; + Texture.load(this, _.bind(function() { + this.loaded = true; + this + .trigger(Two.Events.change) + .trigger(Two.Events.load); + }, this)); + } + + } + + if (this._image && this._image.readyState >= 4) { + this._flagVideo = true; + } + + return this; + + }, + + flagReset: function() { + + this._flagSrc = this._flagImage = this._flagLoaded + = this._flagVideo = this._flagScale = this._flagOffset = false; + + return this; + + } + + }); + + Texture.MakeObservable(Texture.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + var Path = Two.Path; + var Rectangle = Two.Rectangle; + + var Sprite = Two.Sprite = function(path, ox, oy, cols, rows, frameRate) { + + Path.call(this, [ + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor() + ], true); + + this.noStroke(); + this.noFill(); + + if (path instanceof Two.Texture) { + this.texture = path; + } else if (_.isString(path)) { + this.texture = new Two.Texture(path); + } + + this._update(); + this.translation.set(ox || 0, oy || 0); + + if (_.isNumber(cols)) { + this.columns = cols; + } + if (_.isNumber(rows)) { + this.rows = rows; + } + if (_.isNumber(frameRate)) { + this.frameRate = frameRate; + } + + }; + + _.extend(Sprite, { + + Properties: [ + 'texture', 'columns', 'rows', 'frameRate', 'index' + ], + + MakeObservable: function(obj) { + + Rectangle.MakeObservable(obj); + _.each(Sprite.Properties, Two.Utils.defineProperty, obj); + + } + + }) + + _.extend(Sprite.prototype, Rectangle.prototype, { + + _flagTexture: false, + _flagColumns: false, + _flagRows: false, + _flagFrameRate: false, + flagIndex: false, + + // Private variables + _amount: 1, + _duration: 0, + _startTime: 0, + _playing: false, + _firstFrame: 0, + _lastFrame: 0, + _loop: true, + + // Exposed through getter-setter + _texture: null, + _columns: 1, + _rows: 1, + _frameRate: 0, + _index: 0, + + play: function(firstFrame, lastFrame, onLastFrame) { + + this._playing = true; + this._firstFrame = 0; + this._lastFrame = this.amount - 1; + this._startTime = _.performance.now(); + + if (_.isNumber(firstFrame)) { + this._firstFrame = firstFrame; + } + if (_.isNumber(lastFrame)) { + this._lastFrame = lastFrame; + } + if (_.isFunction(onLastFrame)) { + this._onLastFrame = onLastFrame; + } else { + delete this._onLastFrame; + } + + if (this._index !== this._firstFrame) { + this._startTime -= 1000 * Math.abs(this._index - this._firstFrame) + / this._frameRate; + } + + return this; + + }, + + pause: function() { + + this._playing = false; + return this; + + }, + + stop: function() { + + this._playing = false; + this._index = 0; + + return this; + + }, + + clone: function(parent) { + + parent = parent || this.parent; + + var clone = new Sprite( + this.texture, this.translation.x, this.translation.y, + this.columns, this.rows, this.frameRate + ); + + if (this.playing) { + clone.play(this._firstFrame, this._lastFrame); + clone._loop = this._loop; + } + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + _update: function() { + + var effect = this._texture; + var cols = this._columns; + var rows = this._rows; + + var width, height, elapsed, amount, duration; + var index, iw, ih, isRange, frames; + + if (this._flagColumns || this._flagRows) { + this._amount = this._columns * this._rows; + } + + if (this._flagFrameRate) { + this._duration = 1000 * this._amount / this._frameRate; + } + + if (this._flagTexture) { + this.fill = this._texture; + } + + if (this._texture.loaded) { + + iw = effect.image.width; + ih = effect.image.height; + + width = iw / cols; + height = ih / rows; + amount = this._amount; + + if (this.width !== width) { + this.width = width; + } + if (this.height !== height) { + this.height = height; + } + + if (this._playing && this._frameRate > 0) { + + if (_.isNaN(this._lastFrame)) { + this._lastFrame = amount - 1; + } + + // TODO: Offload perf logic to instance of `Two`. + elapsed = _.performance.now() - this._startTime; + frames = this._lastFrame + 1; + duration = 1000 * (frames - this._firstFrame) / this._frameRate; + + if (this._loop) { + elapsed = elapsed % duration; + } else { + elapsed = Math.min(elapsed, duration); + } + + index = _.lerp(this._firstFrame, frames, elapsed / duration); + index = Math.floor(index); + + if (index !== this._index) { + this._index = index; + if (index >= this._lastFrame - 1 && this._onLastFrame) { + this._onLastFrame(); // Shortcut for chainable sprite animations + } + } + + } + + var col = this._index % cols; + var row = Math.floor(this._index / cols); + + var ox = - width * col + (iw - width) / 2; + var oy = - height * row + (ih - height) / 2; + + // TODO: Improve performance + if (ox !== effect.offset.x) { + effect.offset.x = ox; + } + if (oy !== effect.offset.y) { + effect.offset.y = oy; + } + + } + + Rectangle.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + this._flagTexture = this._flagColumns = this._flagRows + = this._flagFrameRate = false; + + Rectangle.prototype.flagReset.call(this); + + return this; + } + + + }); + + Sprite.MakeObservable(Sprite.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + var _ = Two.Utils; + var Path = Two.Path; + var Rectangle = Two.Rectangle; + + var ImageSequence = Two.ImageSequence = function(paths, ox, oy, frameRate) { + + Path.call(this, [ + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor(), + new Two.Anchor() + ], true); + + this._renderer.flagTextures = _.bind(ImageSequence.FlagTextures, this); + this._renderer.bindTextures = _.bind(ImageSequence.BindTextures, this); + this._renderer.unbindTextures = _.bind(ImageSequence.UnbindTextures, this); + + this.noStroke(); + this.noFill(); + + this.textures = _.map(paths, ImageSequence.GenerateTexture, this); + + this._update(); + this.translation.set(ox || 0, oy || 0); + + if (_.isNumber(frameRate)) { + this.frameRate = frameRate; + } else { + this.frameRate = ImageSequence.DefaultFrameRate; + } + + }; + + _.extend(ImageSequence, { + + Properties: [ + 'frameRate', + 'index' + ], + + DefaultFrameRate: 30, + + FlagTextures: function() { + this._flagTextures = true; + }, + + BindTextures: function(items) { + + var i = items.length; + while (i--) { + items[i].bind(Two.Events.change, this._renderer.flagTextures); + } + + this._renderer.flagTextures(); + + }, + + UnbindTextures: function(items) { + + var i = items.length; + while (i--) { + items[i].unbind(Two.Events.change, this._renderer.flagTextures); + } + + this._renderer.flagTextures(); + + }, + + MakeObservable: function(obj) { + + Rectangle.MakeObservable(obj); + _.each(ImageSequence.Properties, Two.Utils.defineProperty, obj); + + Object.defineProperty(obj, 'textures', { + + enumerable: true, + + get: function() { + return this._textures; + }, + + set: function(textures) { + + var updateTextures = this._renderer.flagTextures; + var bindTextures = this._renderer.bindTextures; + var unbindTextures = this._renderer.unbindTextures; + + // Remove previous listeners + if (this._textures) { + this._textures + .unbind(Two.Events.insert, bindTextures) + .unbind(Two.Events.remove, unbindTextures); + } + + // Create new Collection with copy of vertices + this._textures = new Two.Utils.Collection((textures || []).slice(0)); + + // Listen for Collection changes and bind / unbind + this._textures + .bind(Two.Events.insert, bindTextures) + .bind(Two.Events.remove, unbindTextures); + + // Bind Initial Textures + bindTextures(this._textures); + + } + + }); + + }, + + GenerateTexture: function(obj) { + if (obj instanceof Two.Texture) { + return obj; + } else if (_.isString(obj)) { + return new Two.Texture(obj); + } + } + + }); + + _.extend(ImageSequence.prototype, Rectangle.prototype, { + + _flagTextures: false, + _flagFrameRate: false, + _flagIndex: false, + + // Private variables + _amount: 1, + _duration: 0, + _index: 0, + _startTime: 0, + _playing: false, + _firstFrame: 0, + _lastFrame: 0, + _loop: true, + + // Exposed through getter-setter + _textures: null, + _frameRate: 0, + + play: function(firstFrame, lastFrame, onLastFrame) { + + this._playing = true; + this._firstFrame = 0; + this._lastFrame = this.amount - 1; + this._startTime = _.performance.now(); + + if (_.isNumber(firstFrame)) { + this._firstFrame = firstFrame; + } + if (_.isNumber(lastFrame)) { + this._lastFrame = lastFrame; + } + if (_.isFunction(onLastFrame)) { + this._onLastFrame = onLastFrame; + } else { + delete this._onLastFrame; + } + + if (this._index !== this._firstFrame) { + this._startTime -= 1000 * Math.abs(this._index - this._firstFrame) + / this._frameRate; + } + + return this; + + }, + + pause: function() { + + this._playing = false; + return this; + + }, + + stop: function() { + + this._playing = false; + this._index = 0; + + return this; + + }, + + clone: function(parent) { + + parent = parent || this.parent; + + var clone = new ImageSequence(this.textures, this.translation.x, + this.translation.y, this.frameRate) + + clone._loop = this._loop; + + if (this._playing) { + clone.play(); + } + + if (parent) { + parent.add(clone); + } + + return clone; + + }, + + _update: function() { + + var effects = this._textures; + var width, height, elapsed, amount, duration, texture; + var index, frames; + + if (this._flagTextures) { + this._amount = effects.length; + } + + if (this._flagFrameRate) { + this._duration = 1000 * this._amount / this._frameRate; + } + + if (this._playing && this._frameRate > 0) { + + amount = this._amount; + + if (_.isNaN(this._lastFrame)) { + this._lastFrame = amount - 1; + } + + // TODO: Offload perf logic to instance of `Two`. + elapsed = _.performance.now() - this._startTime; + frames = this._lastFrame + 1; + duration = 1000 * (frames - this._firstFrame) / this._frameRate; + + if (this._loop) { + elapsed = elapsed % duration; + } else { + elapsed = Math.min(elapsed, duration); + } + + index = _.lerp(this._firstFrame, frames, elapsed / duration); + index = Math.floor(index); + + if (index !== this._index) { + + this._index = index; + texture = effects[this._index]; + + if (texture.loaded) { + + width = texture.image.width; + height = texture.image.height; + + if (this.width !== width) { + this.width = width; + } + if (this.height !== height) { + this.height = height; + } + + this.fill = texture; + + if (index >= this._lastFrame - 1 && this._onLastFrame) { + this._onLastFrame(); // Shortcut for chainable sprite animations + } + + } + + } + + } else if (this._flagIndex || !(this.fill instanceof Two.Texture)) { + + texture = effects[this._index]; + + if (texture.loaded) { + + width = texture.image.width; + height = texture.image.height; + + if (this.width !== width) { + this.width = width; + } + if (this.height !== height) { + this.height = height; + } + + } + + this.fill = texture; + + } + + Rectangle.prototype._update.call(this); + + return this; + + }, + + flagReset: function() { + + this._flagTextures = this._flagFrameRate = false; + Rectangle.prototype.flagReset.call(this); + + return this; + + } + + }); + + ImageSequence.MakeObservable(ImageSequence.prototype); + +})((typeof global !== 'undefined' ? global : this).Two); + +(function(Two) { + + /** + * Constants + */ + var min = Math.min, max = Math.max; + var _ = Two.Utils; + + /** + * A children collection which is accesible both by index and by object id + * @constructor + */ + var Children = function() { + + Two.Utils.Collection.apply(this, arguments); + + Object.defineProperty(this, '_events', { + value : {}, + enumerable: false + }); + + this.ids = {}; + + this.on(Two.Events.insert, this.attach); + this.on(Two.Events.remove, this.detach); + Children.prototype.attach.apply(this, arguments); + + }; + + Children.prototype = new Two.Utils.Collection(); + Children.prototype.constructor = Children; + + _.extend(Children.prototype, { + + attach: function(children) { + for (var i = 0; i < children.length; i++) { + this.ids[children[i].id] = children[i]; + } + return this; + }, + + detach: function(children) { + for (var i = 0; i < children.length; i++) { + delete this.ids[children[i].id]; + } + return this; + } + + }); + + var Group = Two.Group = function() { + + Two.Shape.call(this, true); + + this._renderer.type = 'group'; + + this.additions = []; + this.subtractions = []; + + this.children = arguments; + + }; + + _.extend(Group, { + + Children: Children, + + InsertChildren: function(children) { + for (var i = 0; i < children.length; i++) { + replaceParent.call(this, children[i], this); + } + }, + + RemoveChildren: function(children) { + for (var i = 0; i < children.length; i++) { + replaceParent.call(this, children[i]); + } + }, + + OrderChildren: function(children) { + this._flagOrder = true; + }, + + MakeObservable: function(object) { + + var properties = Two.Path.Properties.slice(0); + var oi = _.indexOf(properties, 'opacity'); + + if (oi >= 0) { + + properties.splice(oi, 1); + + Object.defineProperty(object, 'opacity', { + + enumerable: true, + + get: function() { + return this._opacity; + }, + + set: function(v) { + // Only set flag if there is an actual difference + this._flagOpacity = (this._opacity != v); + this._opacity = v; + } + + }); + + } + + Two.Shape.MakeObservable(object); + Group.MakeGetterSetters(object, properties); + + Object.defineProperty(object, 'children', { + + enumerable: true, + + get: function() { + return this._children; + }, + + set: function(children) { + + var insertChildren = _.bind(Group.InsertChildren, this); + var removeChildren = _.bind(Group.RemoveChildren, this); + var orderChildren = _.bind(Group.OrderChildren, this); + + if (this._children) { + this._children.unbind(); + } + + this._children = new Children(children); + this._children.bind(Two.Events.insert, insertChildren); + this._children.bind(Two.Events.remove, removeChildren); + this._children.bind(Two.Events.order, orderChildren); + + } + + }); + + Object.defineProperty(object, 'mask', { + + enumerable: true, + + get: function() { + return this._mask; + }, + + set: function(v) { + this._mask = v; + this._flagMask = true; + if (!v.clip) { + v.clip = true; + } + } + + }); + + }, + + MakeGetterSetters: function(group, properties) { + + if (!_.isArray(properties)) { + properties = [properties]; + } + + _.each(properties, function(k) { + Group.MakeGetterSetter(group, k); + }); + + }, + + MakeGetterSetter: function(group, k) { + + var secret = '_' + k; + + Object.defineProperty(group, k, { + + enumerable: true, + + get: function() { + return this[secret]; + }, + + set: function(v) { + this[secret] = v; + _.each(this.children, function(child) { // Trickle down styles + child[k] = v; + }); + } + + }); + + } + + }); + + _.extend(Group.prototype, Two.Shape.prototype, { + + // Flags + // http://en.wikipedia.org/wiki/Flag + + _flagAdditions: false, + _flagSubtractions: false, + _flagOrder: false, + _flagOpacity: true, + + _flagMask: false, + + // Underlying Properties + + _fill: '#fff', + _stroke: '#000', + _linewidth: 1.0, + _opacity: 1.0, + _visible: true, + + _cap: 'round', + _join: 'round', + _miter: 4, + + _closed: true, + _curved: false, + _automatic: true, + _beginning: 0, + _ending: 1.0, + + _mask: null, + + /** + * TODO: Group has a gotcha in that it's at the moment required to be bound to + * an instance of two in order to add elements correctly. This needs to + * be rethought and fixed. + */ + clone: function(parent) { + + parent = parent || this.parent; + + var group = new Group(); + var children = _.map(this.children, function(child) { + return child.clone(group); + }); + + group.add(children); + + group.opacity = this.opacity; + + if (this.mask) { + group.mask = this.mask; + } + + group.translation.copy(this.translation); + group.rotation = this.rotation; + group.scale = this.scale; + + if (parent) { + parent.add(group); + } + + return group; + + }, + + /** + * Export the data from the instance of Two.Group into a plain JavaScript + * object. This also makes all children plain JavaScript objects. Great + * for turning into JSON and storing in a database. + */ + toObject: function() { + + var result = { + children: [], + translation: this.translation.toObject(), + rotation: this.rotation, + scale: this.scale, + opacity: this.opacity, + mask: (this.mask ? this.mask.toObject() : null) + }; + + _.each(this.children, function(child, i) { + result.children[i] = child.toObject(); + }, this); + + return result; + + }, + + /** + * Anchor all children to the upper left hand corner + * of the group. + */ + corner: function() { + + var rect = this.getBoundingClientRect(true), + corner = { x: rect.left, y: rect.top }; + + this.children.forEach(function(child) { + child.translation.subSelf(corner); + }); + + return this; + + }, + + /** + * Anchors all children around the center of the group, + * effectively placing the shape around the unit circle. + */ + center: function() { + + var rect = this.getBoundingClientRect(true); + + rect.centroid = { + x: rect.left + rect.width / 2, + y: rect.top + rect.height / 2 + }; + + this.children.forEach(function(child) { + if (child.isShape) { + child.translation.subSelf(rect.centroid); + } + }); + + // this.translation.copy(rect.centroid); + + return this; + + }, + + /** + * Recursively search for id. Returns the first element found. + * Returns null if none found. + */ + getById: function (id) { + var search = function (node, id) { + if (node.id === id) { + return node; + } else if (node.children) { + var i = node.children.length; + while (i--) { + var found = search(node.children[i], id); + if (found) return found; + } + } + + }; + return search(this, id) || null; + }, + + /** + * Recursively search for classes. Returns an array of matching elements. + * Empty array if none found. + */ + getByClassName: function (cl) { + var found = []; + var search = function (node, cl) { + if (node.classList.indexOf(cl) != -1) { + found.push(node); + } else if (node.children) { + node.children.forEach(function (child) { + search(child, cl); + }); + } + return found; + }; + return search(this, cl); + }, + + /** + * Recursively search for children of a specific type, + * e.g. Two.Polygon. Pass a reference to this type as the param. + * Returns an empty array if none found. + */ + getByType: function(type) { + var found = []; + var search = function (node, type) { + for (var id in node.children) { + if (node.children[id] instanceof type) { + found.push(node.children[id]); + } else if (node.children[id] instanceof Two.Group) { + search(node.children[id], type); + } + } + return found; + }; + return search(this, type); + }, + + /** + * Add objects to the group. + */ + add: function(objects) { + + // Allow to pass multiple objects either as array or as multiple arguments + // If it's an array also create copy of it in case we're getting passed + // a childrens array directly. + if (!(objects instanceof Array)) { + objects = _.toArray(arguments); + } else { + objects = objects.slice(); + } + + // Add the objects + for (var i = 0; i < objects.length; i++) { + if (!(objects[i] && objects[i].id)) continue; + this.children.push(objects[i]); + } + + return this; + + }, + + /** + * Remove objects from the group. + */ + remove: function(objects) { + + var l = arguments.length, + grandparent = this.parent; + + // Allow to call remove without arguments + // This will detach the object from the scene. + if (l <= 0 && grandparent) { + grandparent.remove(this); + return this; + } + + // Allow to pass multiple objects either as array or as multiple arguments + // If it's an array also create copy of it in case we're getting passed + // a childrens array directly. + if (!(objects instanceof Array)) { + objects = _.toArray(arguments); + } else { + objects = objects.slice(); + } + + // Remove the objects + for (var i = 0; i < objects.length; i++) { + if (!objects[i] || !(this.children.ids[objects[i].id])) continue; + this.children.splice(_.indexOf(this.children, objects[i]), 1); + } + + return this; + + }, + + /** + * Return an object with top, left, right, bottom, width, and height + * parameters of the group. + */ + getBoundingClientRect: function(shallow) { + var rect; + + // TODO: Update this to not __always__ update. Just when it needs to. + this._update(true); + + // Variables need to be defined here, because of nested nature of groups. + var left = Infinity, right = -Infinity, + top = Infinity, bottom = -Infinity; + + this.children.forEach(function(child) { + + if (/(linear-gradient|radial-gradient|gradient)/.test(child._renderer.type)) { + return; + } + + rect = child.getBoundingClientRect(shallow); + + if (!_.isNumber(rect.top) || !_.isNumber(rect.left) || + !_.isNumber(rect.right) || !_.isNumber(rect.bottom)) { + return; + } + + top = min(rect.top, top); + left = min(rect.left, left); + right = max(rect.right, right); + bottom = max(rect.bottom, bottom); + + }, this); + + return { + top: top, + left: left, + right: right, + bottom: bottom, + width: right - left, + height: bottom - top + }; + + }, + + /** + * Trickle down of noFill + */ + noFill: function() { + this.children.forEach(function(child) { + child.noFill(); + }); + return this; + }, + + /** + * Trickle down of noStroke + */ + noStroke: function() { + this.children.forEach(function(child) { + child.noStroke(); + }); + return this; + }, + + /** + * Trickle down subdivide + */ + subdivide: function() { + var args = arguments; + this.children.forEach(function(child) { + child.subdivide.apply(child, args); + }); + return this; + }, + + flagReset: function() { + + if (this._flagAdditions) { + this.additions.length = 0; + this._flagAdditions = false; + } + + if (this._flagSubtractions) { + this.subtractions.length = 0; + this._flagSubtractions = false; + } + + this._flagOrder = this._flagMask = this._flagOpacity = false; + + Two.Shape.prototype.flagReset.call(this); + + return this; + + } + + }); + + Group.MakeObservable(Group.prototype); + + /** + * Helper function used to sync parent-child relationship within the + * `Two.Group.children` object. + * + * Set the parent of the passed object to another object + * and updates parent-child relationships + * Calling with one arguments will simply remove the parenting + */ + function replaceParent(child, newParent) { + + var parent = child.parent; + var index; + + if (parent === newParent) { + this.additions.push(child); + this._flagAdditions = true; + return; + } + + if (parent && parent.children.ids[child.id]) { + + index = _.indexOf(parent.children, child); + parent.children.splice(index, 1); + + // If we're passing from one parent to another... + index = _.indexOf(parent.additions, child); + + if (index >= 0) { + parent.additions.splice(index, 1); + } else { + parent.subtractions.push(child); + parent._flagSubtractions = true; + } + + } + + if (newParent) { + child.parent = newParent; + this.additions.push(child); + this._flagAdditions = true; + return; + } + + // If we're passing from one parent to another... + index = _.indexOf(this.additions, child); + + if (index >= 0) { + this.additions.splice(index, 1); + } else { + this.subtractions.push(child); + this._flagSubtractions = true; + } + + delete child.parent; + + } + +})((typeof global !== 'undefined' ? global : this).Two); diff --git a/ceph/ceph/debian/deb_folder/python3-ceph-argparse.install b/ceph/ceph/debian/deb_folder/python3-ceph-argparse.install new file mode 100644 index 000000000..274b8b4f7 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/python3-ceph-argparse.install @@ -0,0 +1,2 @@ +usr/lib/python3*/dist-packages/ceph_argparse.py +usr/lib/python3*/dist-packages/ceph_daemon.py diff --git a/ceph/ceph/debian/deb_folder/python3-cephfs.install b/ceph/ceph/debian/deb_folder/python3-cephfs.install new file mode 100644 index 000000000..6eb883670 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/python3-cephfs.install @@ -0,0 +1,3 @@ +usr/lib/python3*/dist-packages/ceph_volume_client.py +usr/lib/python3*/dist-packages/cephfs-*.egg-info +usr/lib/python3*/dist-packages/cephfs.cpython*.so diff --git a/ceph/ceph/debian/deb_folder/python3-rados.install b/ceph/ceph/debian/deb_folder/python3-rados.install new file mode 100644 index 000000000..98b5d76cb --- /dev/null +++ b/ceph/ceph/debian/deb_folder/python3-rados.install @@ -0,0 +1,2 @@ +usr/lib/python3*/dist-packages/rados-*.egg-info +usr/lib/python3*/dist-packages/rados.cpython*.so diff --git a/ceph/ceph/debian/deb_folder/python3-rbd.install b/ceph/ceph/debian/deb_folder/python3-rbd.install new file mode 100644 index 000000000..5f4e6e143 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/python3-rbd.install @@ -0,0 +1,2 @@ +usr/lib/python3*/dist-packages/rbd-*.egg-info +usr/lib/python3*/dist-packages/rbd.cpython*.so diff --git a/ceph/ceph/debian/deb_folder/python3-rgw.install b/ceph/ceph/debian/deb_folder/python3-rgw.install new file mode 100644 index 000000000..57f455907 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/python3-rgw.install @@ -0,0 +1,2 @@ +usr/lib/python3*/dist-packages/rgw-*.egg-info +usr/lib/python3*/dist-packages/rgw.cpython*.so diff --git a/ceph/ceph/debian/deb_folder/rados-objclass-dev.install b/ceph/ceph/debian/deb_folder/rados-objclass-dev.install new file mode 100644 index 000000000..ac8f90ee2 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rados-objclass-dev.install @@ -0,0 +1 @@ +usr/include/rados/objclass.h diff --git a/ceph/ceph/debian/deb_folder/radosgw.dirs b/ceph/ceph/debian/deb_folder/radosgw.dirs new file mode 100644 index 000000000..b728437e2 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/radosgw.dirs @@ -0,0 +1,4 @@ +var/lib/ceph/radosgw + +# %if %{with stx} +var/log/radosgw diff --git a/ceph/ceph/debian/deb_folder/radosgw.install b/ceph/ceph/debian/deb_folder/radosgw.install new file mode 100644 index 000000000..5795d1145 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/radosgw.install @@ -0,0 +1,17 @@ +# %if %{without stx} +# lib/systemd/system/ceph-radosgw* + +usr/bin/radosgw +usr/bin/radosgw-es +usr/bin/radosgw-object-expirer +usr/bin/radosgw-token +usr/share/man/man8/radosgw.8 + +usr/bin/rgw-gap-list +usr/bin/rgw-gap-list-comparator + +# %if %{with stx} +etc/init.d/ceph-radosgw + +usr/bin/ceph-diff-sorted +usr/bin/rgw-orphan-list diff --git a/ceph/ceph/debian/deb_folder/radosgw.postinst b/ceph/ceph/debian/deb_folder/radosgw.postinst new file mode 100644 index 000000000..3f1551269 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/radosgw.postinst @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +if [ "${1}" = "configure" ] ; then + [ -f "/etc/default/ceph" ] && . /etc/default/ceph + [ -z "$SERVER_USER" ] && SERVER_USER=ceph + [ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + if ! dpkg-statoverride --list /var/lib/ceph/radosgw >/dev/null; then + chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/radosgw + fi +fi +#DEBHELPER# + +exit 0 + + diff --git a/ceph/ceph/debian/deb_folder/radosgw.prerm b/ceph/ceph/debian/deb_folder/radosgw.prerm new file mode 100644 index 000000000..0288ab77b --- /dev/null +++ b/ceph/ceph/debian/deb_folder/radosgw.prerm @@ -0,0 +1,22 @@ +#!/bin/sh +# vim: set noet ts=8: + +set -e + +case "$1" in + remove) + invoke-rc.d radosgw stop || { + RESULT=$? + if [ $RESULT != 100 ]; then + exit $RESULT + fi + } + ;; + + *) + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/ceph/ceph/debian/deb_folder/rbd-fuse.install b/ceph/ceph/debian/deb_folder/rbd-fuse.install new file mode 100644 index 000000000..7b6b96fe7 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rbd-fuse.install @@ -0,0 +1,2 @@ +usr/bin/rbd-fuse +usr/share/man/man8/rbd-fuse.8 diff --git a/ceph/ceph/debian/deb_folder/rbd-mirror.install b/ceph/ceph/debian/deb_folder/rbd-mirror.install new file mode 100644 index 000000000..8fff9890e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rbd-mirror.install @@ -0,0 +1,5 @@ +# %if %{without stx} +# lib/systemd/system/ceph-rbd-mirror* + +usr/bin/rbd-mirror +usr/share/man/man8/rbd-mirror.8 diff --git a/ceph/ceph/debian/deb_folder/rbd-nbd.install b/ceph/ceph/debian/deb_folder/rbd-nbd.install new file mode 100644 index 000000000..385c4501f --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rbd-nbd.install @@ -0,0 +1,2 @@ +usr/bin/rbd-nbd +usr/share/man/man8/rbd-nbd.8 diff --git a/ceph/ceph/debian/deb_folder/rest-bench.install b/ceph/ceph/debian/deb_folder/rest-bench.install new file mode 100644 index 000000000..8535f20d5 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rest-bench.install @@ -0,0 +1 @@ +usr/bin/rest-bench diff --git a/ceph/ceph/debian/deb_folder/rules b/ceph/ceph/debian/deb_folder/rules new file mode 100755 index 000000000..7709b7aff --- /dev/null +++ b/ceph/ceph/debian/deb_folder/rules @@ -0,0 +1,289 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# export DH_VERBOSE=1 + +# Additional files +SOURCE1 := ceph.sh +SOURCE2 := mgr-restful-plugin.py +SOURCE3 := ceph.conf.pmon +SOURCE4 := ceph-init-wrapper.sh +SOURCE5 := ceph.conf +SOURCE6 := ceph-manage-journal.py +SOURCE7 := ceph.service +SOURCE8 := mgr-restful-plugin.service +SOURCE9 := ceph-preshutdown.sh +SOURCE10 := starlingx-docker-override.conf + +# Paths +export DESTDIR = $(CURDIR)/debian/tmp +export INITDIR = etc/init.d +export LIBEXECDIR = usr/lib +export SBINDIR = usr/sbin +export SYSCONFDIR = etc +export UDEVRULESDIR = lib/udev/rules.d +export UNITDIR = lib/systemd/system + +export JAVA_HOME=/usr/lib/jvm/default-java +## Set JAVAC to prevent FTBFS due to incorrect use of 'gcj' if found (see "m4/ac_prog_javac.m4"). +export JAVAC=javac + +DEB_HOST_ARCH_BITS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_BITS) +export DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) +export DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) + +# support ccache for faster build +# cmake uses /usr/bin/c* +ifeq (yes,$(findstring yes,$(shell test -L /usr/lib/ccache/c++ && test -L /usr/lib/ccache/cc && echo -n yes))) + extraopts += -DWITH_CCACHE=ON +endif + +# try to save even more memory on some architectures +# see #849657 for hints. +# Reduce size of debug symbols to fix FTBFS due to the +# 2GB/3GB address space limits on 32bit +ifeq (32,$(DEB_HOST_ARCH_BITS)) + export DEB_CFLAGS_MAINT_APPEND = -g1 + export DEB_CXXFLAGS_MAINT_APPEND = -g1 +endif + +# we don't have NEON on armel. +ifeq ($(DEB_HOST_ARCH),armel) + extraopts += -DHAVE_ARM_NEON=0 +endif + +# disable ceph-dencoder on 32bit except i386 to avoid g++ oom +ifneq (,$(filter $(DEB_HOST_ARCH), armel armhf hppa m68k mips mipsel powerpc sh4 x32)) + extraopts += -DDISABLE_DENCODER=1 +endif + +ifeq ($(shell dpkg-vendor --is Ubuntu && echo yes) $(DEB_HOST_ARCH), yes i386) + skip_packages = -Nceph -Nceph-base -Nceph-mds -Nceph-mgr -Nceph-mon -Nceph-osd +endif + +# minimise needless linking and link to libatomic +# The last is needed because long long atomic operations are not directly +# supported by all processor architectures +export DEB_LDFLAGS_MAINT_APPEND= -Wl,--as-needed -latomic + +# Enable hardening +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# STX CONFIG +extraopts += -DCEPH_SYSTEMD_ENV_DIR=/etc/default +extraopts += -DCMAKE_BUILD_TYPE=Release +extraopts += -DCMAKE_INSTALL_INITCEPH=/$(INITDIR) +extraopts += -DCMAKE_INSTALL_LIBEXECDIR=/$(LIBEXECDIR) +extraopts += -DCMAKE_INSTALL_SYSCONFDIR=/$(SYSCONFDIR) +extraopts += -DCMAKE_INSTALL_SYSTEMD_SERVICEDIR=/$(UNITDIR) +extraopts += -DMGR_PYTHON_VERSION=3 +extraopts += -DWITH_BABELTRACE=OFF +extraopts += -DWITH_CEPHFS=ON +extraopts += -DWITH_CEPHFS_JAVA=ON +extraopts += -DWITH_CEPHFS_SHELL=ON +extraopts += -DWITH_CEPH_TEST_PACKAGE=OFF +extraopts += -DWITH_CLIENT=ON +extraopts += -DWITH_COVERAGE=OFF +extraopts += -DWITH_CRYPTOPP=OFF +extraopts += -DWITH_CYTHON=ON +extraopts += -DWITH_DEBUG=OFF +extraopts += -DWITH_EMBEDDED=OFF +extraopts += -DWITH_EVENTFD=ON +extraopts += -DWITH_FUSE=ON +extraopts += -DWITH_GITVERSION=ON +extraopts += -DWITH_GRAFANA=ON +extraopts += -DWITH_JEMALLOC=OFF +extraopts += -DWITH_KINETIC=OFF +extraopts += -DWITH_LIBAIO=ON +extraopts += -DWITH_LIBATOMIC_OPS=ON +extraopts += -DWITH_LIBROCKSDB=OFF +extraopts += -DWITH_LIBXFS=ON +extraopts += -DWITH_LIBZFS=OFF +extraopts += -DWITH_LTTNG=OFF +extraopts += -DWITH_MAKE_CHECK=OFF +extraopts += -DWITH_MAN_PAGES=OFF +extraopts += -DWITH_MDS=ON +extraopts += -DWITH_MGR_DASHBOARD_FRONTEND=OFF +extraopts += -DWITH_MON=ON +extraopts += -DWITH_NSS=ON +extraopts += -DWITH_OCF=ON +extraopts += -DWITH_OPENLDAP=ON +extraopts += -DWITH_OSD=ON +extraopts += -DWITH_PGREFDEBUGGING=OFF +extraopts += -DWITH_PROFILER=OFF +extraopts += -DWITH_PYTHON2=OFF +extraopts += -DWITH_PYTHON3=ON +extraopts += -DWITH_RADOS=ON +extraopts += -DWITH_RADOSGW=ON +extraopts += -DWITH_RADOSSTRIPER=ON +extraopts += -DWITH_RBD=ON +extraopts += -DWITH_SEASTAR=OFF +extraopts += -DWITH_SELINUX=OFF +extraopts += -DWITH_SERVER=ON +# Disable SPDK as it generates a build which is no compatible +# with older CPU's which are still supported by Ubuntu. +extraopts += -DWITH_SPDK=OFF +extraopts += -DWITH_SUBMAN=OFF +extraopts += -DWITH_SYSTEMD=ON +extraopts += -DWITH_SYSTEM_BOOST=ON +extraopts += -DWITH_TCMALLOC=ON +extraopts += -DWITH_TESTS=OFF +extraopts += -DWITH_VALGRIND=OFF +extraopts += -DWITH_XIO=OFF + +ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + extraopts += -DBOOST_J=$(NUMJOBS) +endif + +ifneq (,$(filter $(DEB_HOST_ARCH),s390x mips64el ia64 m68k ppc64 riscv64 sh4 sparc64 x32 alpha)) + # beast depends on libboost_{context,coroutine} which is not supported on s390x + extraopts += -DWITH_BOOST_CONTEXT=OFF +else + extraopts += -DWITH_BOOST_CONTEXT=ON +endif + +MAX_PARALLEL ?= $(shell ./debian/calc-max-parallel.sh) + +%: + dh $@ --buildsystem=cmake --with javahelper,python3 $(MAX_PARALLEL) + +override_dh_auto_configure: + env | sort + dh_auto_configure --buildsystem=cmake -- $(extraopts) + +override_dh_auto_install: + dh_auto_install --buildsystem=cmake --destdir=$(DESTDIR) + if [ ! -f $(DESTDIR)/usr/bin/ceph-dencoder ]; then \ + cp debian/workarounds/ceph-dencoder-oom $(DESTDIR)/usr/bin/ceph-dencoder ;\ + chmod 755 $(DESTDIR)/usr/bin/ceph-dencoder ;\ + fi + + # udev rules + install -d -m 755 $(DESTDIR)/$(UDEVRULESDIR)/ + install -D -m 644 udev/50-rbd.rules $(DESTDIR)/$(UDEVRULESDIR)/ + install -D -m 640 udev/60-ceph-by-parttypeuuid.rules $(DESTDIR)/$(UDEVRULESDIR)/ + # if %{without stx} + # install -D -m 644 udev/95-ceph-osd.rules $(DESTDIR)/$(UDEVRULESDIR)/ + + # sudoers.d + install -m 0440 -D sudoers.d/ceph-osd-smartctl $(DESTDIR)/etc/sudoers.d/ceph-osd-smartctl + + install -D -m 640 src/etc-rbdmap $(DESTDIR)/etc/ceph/rbdmap + install -D -m 644 etc/sysctl/90-ceph-osd.conf $(DESTDIR)/etc/sysctl.d/30-ceph-osd.conf + + # NOTE: ensure that any versioned erasure coding test code is dropped + # from the package install - package ships unversioned modules. + rm -f $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code/libec_*.so.* + find $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code -type l -delete || : + + # if %{with stx} + install -d -m 750 $(DESTDIR)/${SYSCONFDIR}/services.d/controller/ + install -d -m 750 $(DESTDIR)/${SYSCONFDIR}/services.d/storage/ + install -d -m 750 $(DESTDIR)/${SYSCONFDIR}/services.d/worker/ + mkdir -p $(DESTDIR)/${INITDIR}/ + mkdir -p $(DESTDIR)/${SYSCONFDIR}/ceph/ + mkdir -p $(DESTDIR)/${UNITDIR}/ + install -D -m 750 ${SOURCE1} $(DESTDIR)/${SYSCONFDIR}/services.d/controller/ + install -D -m 750 ${SOURCE1} $(DESTDIR)/${SYSCONFDIR}/services.d/storage/ + install -D -m 750 ${SOURCE1} $(DESTDIR)/${SYSCONFDIR}/services.d/worker/ + install -D -m 750 ${SOURCE2} $(DESTDIR)/${INITDIR}/mgr-restful-plugin + install -D -m 750 ${SOURCE3} $(DESTDIR)/${SYSCONFDIR}/ceph/ + install -D -m 750 ${SOURCE4} $(DESTDIR)/${INITDIR}/ceph-init-wrapper + install -D -m 640 ${SOURCE5} $(DESTDIR)/${SYSCONFDIR}/ceph/ + install -D -m 700 ${SOURCE6} $(DESTDIR)/${SBINDIR}/ceph-manage-journal + install -D -m 644 ${SOURCE7} $(DESTDIR)/${UNITDIR}/ceph.service + install -D -m 644 ${SOURCE8} $(DESTDIR)/${UNITDIR}/mgr-restful-plugin.service + install -D -m 700 ${SOURCE9} $(DESTDIR)/${SBINDIR}/ceph-preshutdown.sh + install -D -m 644 ${SOURCE10} $(DESTDIR)/${UNITDIR}/docker.service.d/starlingx-docker-override.conf + install -m 750 src/init-radosgw $(DESTDIR)/${INITDIR}/ceph-radosgw + sed -i '/### END INIT INFO/a SYSTEMCTL_SKIP_REDIRECT=1' $(DESTDIR)/${INITDIR}/ceph-radosgw + install -m 750 src/init-rbdmap $(DESTDIR)/${INITDIR}/rbdmap + install -d -m 750 $(DESTDIR)/var/log/radosgw + + # if %{without stx} + # install -m 0644 -D systemd/50-ceph.preset $(DESTDIR)/${LIBEXECDIR}/systemd/system-preset/50-ceph.preset + +# doc/changelog is a directory, which confuses dh_installchangelogs +override_dh_installchangelogs: + dh_installchangelogs --exclude doc/changelog + +override_dh_installlogrotate: + cp src/logrotate.conf debian/ceph-common.logrotate + dh_installlogrotate -pceph-common + +override_dh_installinit: + cp src/init-radosgw debian/radosgw.init + dh_installinit --no-start + dh_installinit -pceph-common --name=rbdmap --no-start + dh_installinit -pceph-base --name ceph --no-start + # install the systemd stuff manually since we have funny service names + # and need to update the paths in all of the files post install + # systemd:ceph-common + install -d -m0755 debian/ceph-common/usr/lib/tmpfiles.d + + # if %{without stx} + # install -m 0644 -D systemd/ceph.tmpfiles.d debian/ceph-common/usr/lib/tmpfiles.d/ceph.conf + + # NOTE(jamespage): Install previous ceph-mon service from packaging for upgrades + + # Excluded, as per "files mon" section in ceph.spec for when %{with stx} is on. + # install -d -m0755 debian/ceph-mon/lib/systemd/system + # install -m0644 debian/lib-systemd/system/ceph-mon.service debian/ceph-mon/lib/systemd/system + + # Ensure Debian/Ubuntu specific systemd units are NOT automatically enabled and started + # Enable systemd targets only + dh_systemd_enable -Xceph-mon.service -Xceph-osd.service -X ceph-mds.service + # Start systemd targets only + dh_systemd_start --no-stop-on-upgrade --no-restart-after-upgrade + +override_dh_systemd_enable: + # systemd enable done as part of dh_installinit + +override_dh_systemd_start: + # systemd start done as part of dh_installinit + +override_dh_makeshlibs: + # exclude jni libraries in libcephfs-jni to avoid pointless ldconfig + # calls in maintainer scripts; exclude private erasure-code plugins. + dh_makeshlibs -V -X/usr/lib/jni -X/usr/lib/$(DEB_HOST_MULTIARCH)/ceph/erasure-code + +override_dh_auto_test: + # do not run tests + +override_dh_shlibdeps: + dh_shlibdeps -a --exclude=erasure-code --exclude=rados-classes --exclude=compressor + +override_dh_python3: + for binding in rados cephfs rbd rgw; do \ + dh_python3 -p python3-$$binding --shebang=/usr/bin/python3; \ + done + dh_python3 -p python3-ceph-argparse --shebang=/usr/bin/python3 + dh_python3 -p ceph-common --shebang=/usr/bin/python3 + dh_python3 -p ceph-base --shebang=/usr/bin/python3 + dh_python3 -p ceph-osd --shebang=/usr/bin/python3 + dh_python3 -p ceph-mgr --shebang=/usr/bin/python3 + dh_python3 -p cephfs-shell --shebang=/usr/bin/python3 + +override_dh_builddeb: + dh_builddeb ${skip_packages} + +override_dh_gencontrol: + dh_gencontrol ${skip_packages} + +override_dh_fixperms: + dh_fixperms \ + -Xceph.sh \ + -Xmgr-restful-plugin \ + -Xceph.conf.pmon \ + -Xceph-init-wrapper \ + -Xceph.conf \ + -Xceph-manage-journal \ + -Xceph.service \ + -Xmgr-restful-plugin.service \ + -Xceph-preshutdown.sh \ + -Xstarlingx-docker-override.conf \ + -Xceph-radosgw \ + -Xrbdmap \ + -Xradosgw \ + -X60-ceph-by-parttypeuuid.rules \ + -Xceph-osd-smartctl diff --git a/ceph/ceph/debian/deb_folder/source.lintian-overrides b/ceph/ceph/debian/deb_folder/source.lintian-overrides new file mode 100644 index 000000000..bd5348724 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/source.lintian-overrides @@ -0,0 +1,16 @@ +license-problem-json-evil src/rapidjson/license.txt +wayward-symbolic-link-target-in-source .git -> ../../../../.repo/projects/cgcs-root/stx/git/ceph.git +source-is-missing qa/workunits/erasure-code/jquery.flot.js line length is 3134 characters (>512) +source-is-missing src/civetweb/examples/_obsolete/docroot/jquery.js line length is 517 characters (>512) +source-is-missing src/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_initjs_min.js +source-is-missing src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_initjs_min.js +source-is-missing src/civetweb/test/ajax/jquery.js line length is 32404 characters (>512) +source-contains-prebuilt-windows-binary ceph-erasure-code-corpus/v0.86-310/plugin=isa stripe-width=10000 technique=reed_sol_van k=7 m=3/8 +source-contains-prebuilt-windows-binary ceph-erasure-code-corpus/v0.86-310/plugin=isa stripe-width=10000 technique=reed_sol_van k=7 m=4/8 +source-contains-prebuilt-windows-binary ceph-erasure-code-corpus/v0.86-310/plugin=isa stripe-width=10000 technique=reed_sol_van k=7 m=5/8 +source-contains-prebuilt-windows-binary ceph-erasure-code-corpus/v0.92-988/plugin=isa stripe-width=10000 technique=reed_sol_van k=7 m=3/8 +source-contains-prebuilt-windows-binary ceph-erasure-code-corpus/v0.92-988/plugin=isa stripe-width=10000 technique=reed_sol_van k=7 m=4/8 +source-contains-prebuilt-windows-binary ceph-object-corpus/archive/0.80-rc1-35-g4812150/objects/MOSDPGCreate/a47ae3cd3ba843424dedce0c05308bb5 +source-contains-prebuilt-windows-binary ceph-object-corpus/archive/11.2.0-280-g34e04de/objects/bluestore_extent_ref_map_t::record_t/e800965f13053b760af9612e91333fe4 +source-contains-prebuilt-windows-binary ceph-object-corpus/archive/11.2.0-280-g34e04de/objects/inodeno_t/d849f83485b1a2c4343515901334f02e +source-contains-prebuilt-windows-binary ceph-object-corpus/archive/11.2.0-280-g34e04de/objects/utime_t/00a9a0a9e1f88f5bb148fd729330db51 diff --git a/ceph/ceph/debian/deb_folder/source/format b/ceph/ceph/debian/deb_folder/source/format new file mode 100644 index 000000000..163aaf8d8 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/ceph/ceph/debian/deb_folder/source/options b/ceph/ceph/debian/deb_folder/source/options new file mode 100644 index 000000000..d029bb02e --- /dev/null +++ b/ceph/ceph/debian/deb_folder/source/options @@ -0,0 +1,11 @@ +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock\.sln" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vcproj" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vsprops" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vcxproj" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock_config.props" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.*\.cbproj" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_all\.cc" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_link\.cc" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest\.groupproj" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.*\.vcproj" +extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.*\.sln" diff --git a/ceph/ceph/debian/deb_folder/tests/build-rados b/ceph/ceph/debian/deb_folder/tests/build-rados new file mode 100755 index 000000000..c62999251 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/tests/build-rados @@ -0,0 +1,31 @@ +#!/bin/sh +# autopkgtest check: Build and run a program against librados2 to +# validate that headers are installed and libraries exists + +set -e + +WORKDIR=$(mktemp -d) +trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM +cd $WORKDIR +cat < radostest.c +#include + +int +main(void) +{ + int err; + rados_t cluster; + + err = rados_create(&cluster, NULL); + if (err < 0) { + return (1); + } + return(0); +} +EOF + +gcc -o radostest radostest.c -lrados +echo "build: OK" +[ -x radostest ] +./radostest +echo "run: OK" diff --git a/ceph/ceph/debian/deb_folder/tests/build-rbd b/ceph/ceph/debian/deb_folder/tests/build-rbd new file mode 100755 index 000000000..5ad6f8823 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/tests/build-rbd @@ -0,0 +1,24 @@ +#!/bin/sh +# autopkgtest check: Build and run a program against librbd1 to +# validate that headers are installed and libraries exists + +set -e + +WORKDIR=$(mktemp -d) +trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM +cd $WORKDIR +cat < rbdtest.c +#include + +int +main(void) +{ + return(0); +} +EOF + +gcc -o rbdtest rbdtest.c -lrbd +echo "build: OK" +[ -x rbdtest ] +./rbdtest +echo "run: OK" diff --git a/ceph/ceph/debian/deb_folder/tests/ceph-client b/ceph/ceph/debian/deb_folder/tests/ceph-client new file mode 100755 index 000000000..c693a5698 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/tests/ceph-client @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +CLIENTS=('ceph') + +for client in "${CLIENTS[@]}"; do + echo -n "Testing client $client: " + $client -v 2>&1 > /dev/null + echo "OK" +done diff --git a/ceph/ceph/debian/deb_folder/tests/control b/ceph/ceph/debian/deb_folder/tests/control new file mode 100644 index 000000000..372a057d0 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/tests/control @@ -0,0 +1,9 @@ +Tests: ceph-client build-rados build-rbd python-ceph +Depends: + build-essential, + ceph-common, + librados-dev, + librbd-dev, + python3-rados, + python3-rbd, +Restrictions: needs-root diff --git a/ceph/ceph/debian/deb_folder/tests/python-ceph b/ceph/ceph/debian/deb_folder/tests/python-ceph new file mode 100755 index 000000000..006dbe63d --- /dev/null +++ b/ceph/ceph/debian/deb_folder/tests/python-ceph @@ -0,0 +1,7 @@ +#!/usr/bin/python3 + +# Test that rbd and rados can be imported OK +import rbd +import rados + +print("python-ceph: OK") diff --git a/ceph/ceph/debian/deb_folder/udev/95-ceph-osd-lvm.rules b/ceph/ceph/debian/deb_folder/udev/95-ceph-osd-lvm.rules new file mode 100644 index 000000000..00feae51f --- /dev/null +++ b/ceph/ceph/debian/deb_folder/udev/95-ceph-osd-lvm.rules @@ -0,0 +1,13 @@ +# OSD LVM layout example +# VG prefix: ceph- +# LV prefix: osd- +ACTION=="add", SUBSYSTEM=="block", \ + ENV{DEVTYPE}=="disk", \ + ENV{DM_LV_NAME}=="osd-*", \ + ENV{DM_VG_NAME}=="ceph-*", \ + OWNER:="ceph", GROUP:="ceph", MODE:="660" +ACTION=="change", SUBSYSTEM=="block", \ + ENV{DEVTYPE}=="disk", \ + ENV{DM_LV_NAME}=="osd-*", \ + ENV{DM_VG_NAME}=="ceph-*", \ + OWNER="ceph", GROUP="ceph", MODE="660" diff --git a/ceph/ceph/debian/deb_folder/workarounds/ceph-dencoder-oom b/ceph/ceph/debian/deb_folder/workarounds/ceph-dencoder-oom new file mode 100644 index 000000000..872021328 --- /dev/null +++ b/ceph/ceph/debian/deb_folder/workarounds/ceph-dencoder-oom @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "Unfortunately it is not possible to compile ceph-dencoder" 1>&2 +echo "on this architecture, see bug #947886." 1>&2 +echo "" 1>&2 +exit 1 + diff --git a/ceph/ceph/debian/dl_hook b/ceph/ceph/debian/dl_hook new file mode 100755 index 000000000..c4a3a91f5 --- /dev/null +++ b/ceph/ceph/debian/dl_hook @@ -0,0 +1,101 @@ +#!/bin/bash + +set -x + +STX_BASE=$(realpath ${MY_REPO}/stx) +PKG_BASE=$(realpath ${STX_BASE}/integ/ceph/ceph) + +PKG_SRC_DIR=/ceph + +PKG_BUILD_NAME=$1 +PKG_BUILD_DIR=$(realpath `pwd`/${PKG_BUILD_NAME}) + +# Package up the git source +mkdir ${PKG_BUILD_NAME} +tar -acvvzf - -C ${STX_GIT_SRC_DIR} ceph | tar -xvvzf - --directory ${PKG_BUILD_DIR} --strip-components 1 + +# Remove upstream's debian folder +rm -rf ${PKG_BUILD_DIR}/debian + +# Additional files +cp ${PKG_BASE}/files/* ${PKG_BUILD_DIR}/ + +# External source modules +SOURCE1='boost_1_72_0.tar.bz2' +SOURCE2='ceph-object-corpus/ceph-object-corpus-e32bf8ca3dc6151ebe7f205ba187815bc18e1cef.tar.gz' +SOURCE3='src/civetweb/civetweb-bb99e93da00c3fe8c6b6a98520fb17cf64710ce7.tar.gz' +SOURCE4='src/erasure-code/jerasure/jerasure/jerasure-96c76b89d661c163f65a014b8042c9354ccf7f31.tar.gz' +SOURCE5='src/erasure-code/jerasure/gf-complete/gf-complete-7e61b44404f0ed410c83cfd3947a52e88ae044e1.tar.gz' +SOURCE6='src/rocksdb/rocksdb-4c736f177851cbf9fb7a6790282306ffac5065f8.tar.gz' +SOURCE7='ceph-erasure-code-corpus/ceph-erasure-code-corpus-2d7d78b9cc52e8a9529d8cc2d2954c7d375d5dd7.tar.gz' +SOURCE8='src/spdk/spdk-fd292c568f72187e172b98074d7ccab362dae348.tar.gz' +SOURCE9='src/xxHash/xxHash-1f40c6511fa8dd9d2e337ca8c9bc18b3e87663c9.tar.gz' +SOURCE10='src/isa-l/isa-l-7e1a337433a340bc0974ed0f04301bdaca374af6.tar.gz' +SOURCE11='src/lua/lua-1fce39c6397056db645718b8f5821571d97869a4.tar.gz' +SOURCE12='src/blkin/blkin-f24ceec055ea236a093988237a9821d145f5f7c8.tar.gz' +SOURCE13='src/rapidjson/rapidjson-f54b0e47a08782a6131cc3d60f94d038fa6e0a51.tar.gz' +SOURCE14='src/googletest/googletest-fdb850479284e2aae047b87df6beae84236d0135.tar.gz' +SOURCE15='src/crypto/isa-l/isa-l_crypto/isa-l_crypto-603529a4e06ac8a1662c13d6b31f122e21830352.tar.gz' +SOURCE16='src/zstd/zstd-b706286adbba780006a47ef92df0ad7a785666b6.tar.gz' +SOURCE17='src/spdk/dpdk/dpdk-96fae0e24c9088d9690c38098b25646f861a664b.tar.gz' +SOURCE18='src/rapidjson/thirdparty/gtest/googletest-0a439623f75c029912728d80cb7f1b8b48739ca4.tar.gz' +SOURCE19='src/c-ares/c-ares-fd6124c74da0801f23f9d324559d8b66fb83f533.tar.gz' +SOURCE20='src/dmclock/dmclock-4496dbc6515db96e08660ac38883329c5009f3e9.tar.gz' +SOURCE21='src/seastar/seastar-0cf6aa6b28d69210b271489c0778f226cde0f459.tar.gz' +SOURCE22='src/spawn/spawn-5f4742f647a5a33b9467f648a3968b3cd0a681ee.tar.gz' +SOURCE23='src/spdk/intel-ipsec-mb/intel-ipsec-mb-134c90c912ea9376460e9d949bb1319a83a9d839.tar.gz' +SOURCE24='src/seastar/dpdk/dpdk-a1774652fbbb1fe7c0ff392d5e66de60a0154df6.tar.gz' +SOURCE25='src/seastar/fmt/fmt-80021e25971e44bb6a6d187c0dac8a1823436d80.tar.gz' + +mkdir -p ${PKG_BUILD_DIR}/src/boost +BOOST_VERSION=$(basename "${SOURCE1}" | sed 's/boost_\(.*\)\.tar\.bz2/\1/') +tar xjf $(basename "${SOURCE1}") -C ${PKG_BUILD_DIR}/src/boost \ + --exclude="$BOOST_VERSION/libs/*/doc" \ + --exclude="$BOOST_VERSION/libs/*/example" \ + --exclude="$BOOST_VERSION/libs/*/examples" \ + --exclude="$BOOST_VERSION/libs/*/meta" \ + --exclude="$BOOST_VERSION/libs/*/test" \ + --exclude="$BOOST_VERSION/tools/boostbook" \ + --exclude="$BOOST_VERSION/tools/quickbook" \ + --exclude="$BOOST_VERSION/tools/auto_index" \ + --exclude='doc' \ + --exclude='more' \ + --exclude='status' \ + --strip-components 1 + + +unpack_submodule() { + mkdir -p "${PKG_BUILD_DIR}/$2" && tar xzf $(basename "$1") -C "${PKG_BUILD_DIR}/$2" --strip-components 1 +} + +unpack_submodule "${SOURCE2}" "$(dirname ${SOURCE2})" +unpack_submodule "${SOURCE3}" "$(dirname ${SOURCE3})" +unpack_submodule "${SOURCE4}" "$(dirname ${SOURCE4})" +unpack_submodule "${SOURCE5}" "$(dirname ${SOURCE5})" +unpack_submodule "${SOURCE6}" "$(dirname ${SOURCE6})" +unpack_submodule "${SOURCE7}" "$(dirname ${SOURCE7})" +unpack_submodule "${SOURCE8}" "$(dirname ${SOURCE8})" +unpack_submodule "${SOURCE9}" "$(dirname ${SOURCE9})" +unpack_submodule "${SOURCE10}" "$(dirname ${SOURCE10})" +unpack_submodule "${SOURCE11}" "$(dirname ${SOURCE11})" +unpack_submodule "${SOURCE12}" "$(dirname ${SOURCE12})" +unpack_submodule "${SOURCE13}" "$(dirname ${SOURCE13})" +unpack_submodule "${SOURCE14}" "$(dirname ${SOURCE14})" +unpack_submodule "${SOURCE15}" "$(dirname ${SOURCE15})" +unpack_submodule "${SOURCE16}" "$(dirname ${SOURCE16})" +unpack_submodule "${SOURCE17}" "$(dirname ${SOURCE17})" +unpack_submodule "${SOURCE18}" "$(dirname ${SOURCE18})" +unpack_submodule "${SOURCE19}" "$(dirname ${SOURCE19})" +unpack_submodule "${SOURCE20}" "$(dirname ${SOURCE20})" +unpack_submodule "${SOURCE21}" "$(dirname ${SOURCE21})" +unpack_submodule "${SOURCE22}" "$(dirname ${SOURCE22})" +unpack_submodule "${SOURCE23}" "$(dirname ${SOURCE23})" +unpack_submodule "${SOURCE24}" "$(dirname ${SOURCE24})" +unpack_submodule "${SOURCE25}" "$(dirname ${SOURCE25})" + +# Alpine configs +sed -e "s/@VERSION@/14.2.22/g" \ + -e "s/@RPM_RELEASE@/1.stx.1/g" \ + -e "s/@TARBALL_BASENAME@/ceph-14.2.22/g" \ + -i ${PKG_BUILD_DIR}/alpine/APKBUILD.in +mv ${PKG_BUILD_DIR}/alpine/APKBUILD.in ${PKG_BUILD_DIR}/alpine/APKBUILD diff --git a/ceph/ceph/debian/meta_data.yaml b/ceph/ceph/debian/meta_data.yaml new file mode 100644 index 000000000..75d81d9ad --- /dev/null +++ b/ceph/ceph/debian/meta_data.yaml @@ -0,0 +1,108 @@ +--- +debname: ceph +debver: 14.2.22 +dl_hook: dl_hook +dl_files: + boost_1_72_0.tar.gz: + topdir: boost_1_72_0 + url: https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz + md5sum: e2b0b1eac302880461bcbef097171758 + ceph-object-corpus-e32bf8ca3dc6151ebe7f205ba187815bc18e1cef.tar.gz: + topdir: ceph-object-corpus + url: https://api.github.com/repos/ceph/ceph-object-corpus/tarball/e32bf8ca3dc6151ebe7f205ba187815bc18e1cef + md5sum: + civetweb-bb99e93da00c3fe8c6b6a98520fb17cf64710ce7.tar.gz: + topdir: civetweb + url: https://api.github.com/repos/ceph/civetweb/tarball/bb99e93da00c3fe8c6b6a98520fb17cf64710ce7 + md5sum: a110885c75cac0435f57b56f8e32df57 + jerasure-96c76b89d661c163f65a014b8042c9354ccf7f31.tar.gz: + topdir: jerasure + url: https://api.github.com/repos/ceph/jerasure/tarball/96c76b89d661c163f65a014b8042c9354ccf7f31 + md5sum: + gf-complete-7e61b44404f0ed410c83cfd3947a52e88ae044e1.tar.gz: + topdir: gf-complete + url: https://api.github.com/repos/ceph/gf-complete/tarball/7e61b44404f0ed410c83cfd3947a52e88ae044e1 + md5sum: + rocksdb-4c736f177851cbf9fb7a6790282306ffac5065f8.tar.gz: + topdir: rocksdb + url: https://api.github.com/repos/ceph/rocksdb/tarball/4c736f177851cbf9fb7a6790282306ffac5065f8 + md5sum: 04631f16668bca46b7baf56ffc176ddd + ceph-erasure-code-corpus-2d7d78b9cc52e8a9529d8cc2d2954c7d375d5dd7.tar.gz: + topdir: ceph-erasure-code-corpus + url: https://api.github.com/repos/ceph/ceph-erasure-code-corpus/tarball/2d7d78b9cc52e8a9529d8cc2d2954c7d375d5dd7 + md5sum: + spdk-fd292c568f72187e172b98074d7ccab362dae348.tar.gz: + topdir: spdk + url: https://api.github.com/repos/ceph/spdk/tarball/fd292c568f72187e172b98074d7ccab362dae348 + md5sum: 36865710c40e9be09c86897e385a275f + xxHash-1f40c6511fa8dd9d2e337ca8c9bc18b3e87663c9.tar.gz: + topdir: xxHash + url: https://api.github.com/repos/ceph/xxHash/tarball/1f40c6511fa8dd9d2e337ca8c9bc18b3e87663c9 + md5sum: + isa-l-7e1a337433a340bc0974ed0f04301bdaca374af6.tar.gz: + topdir: isa-l + url: https://api.github.com/repos/ceph/isa-l/tarball/7e1a337433a340bc0974ed0f04301bdaca374af6 + md5sum: + lua-1fce39c6397056db645718b8f5821571d97869a4.tar.gz: + topdir: lua + url: https://api.github.com/repos/ceph/lua/tarball/1fce39c6397056db645718b8f5821571d97869a4 + md5sum: + blkin-f24ceec055ea236a093988237a9821d145f5f7c8.tar.gz: + topdir: blkin + url: https://api.github.com/repos/ceph/blkin/tarball/f24ceec055ea236a093988237a9821d145f5f7c8 + md5sum: + rapidjson-f54b0e47a08782a6131cc3d60f94d038fa6e0a51.tar.gz: + topdir: rapidjson + url: https://api.github.com/repos/ceph/rapidjson/tarball/f54b0e47a08782a6131cc3d60f94d038fa6e0a51 + md5sum: + googletest-fdb850479284e2aae047b87df6beae84236d0135.tar.gz: + topdir: googletest + url: https://api.github.com/repos/ceph/googletest/tarball/fdb850479284e2aae047b87df6beae84236d0135 + md5sum: + isa-l_crypto-603529a4e06ac8a1662c13d6b31f122e21830352.tar.gz: + topdir: isa-l_crypto + url: https://api.github.com/repos/01org/isa-l_crypto/tarball/603529a4e06ac8a1662c13d6b31f122e21830352 + md5sum: + zstd-b706286adbba780006a47ef92df0ad7a785666b6.tar.gz: + topdir: zstd + url: https://api.github.com/repos/facebook/zstd/tarball/b706286adbba780006a47ef92df0ad7a785666b6 + md5sum: a7acf287326438fe7ad1fa5a23a0e037 + dpdk-96fae0e24c9088d9690c38098b25646f861a664b.tar.gz: + topdir: dpdk-96fae0e24c9088d9690c38098b25646f861a664b + url: https://api.github.com/repos/spdk/dpdk/tarball/96fae0e24c9088d9690c38098b25646f861a664b + md5sum: f0fe4b97cfaa9e707b376e3bbf1f48bd + googletest-0a439623f75c029912728d80cb7f1b8b48739ca4.tar.gz: + topdir: googletest + url: https://api.github.com/repos/google/googletest/tarball/0a439623f75c029912728d80cb7f1b8b48739ca4 + md5sum: + c-ares-fd6124c74da0801f23f9d324559d8b66fb83f533.tar.gz: + topdir: c-ares + url: https://api.github.com/repos/ceph/c-ares/tarball/fd6124c74da0801f23f9d324559d8b66fb83f533 + md5sum: cb24a561f32368a75f62275a527bc2fe + dmclock-4496dbc6515db96e08660ac38883329c5009f3e9.tar.gz: + topdir: dmclock + url: https://api.github.com/repos/ceph/dmclock/tarball/4496dbc6515db96e08660ac38883329c5009f3e9 + md5sum: dc7fab617c6b6ccf5fe8f2691a62ee5e + seastar-0cf6aa6b28d69210b271489c0778f226cde0f459.tar.gz: + topdir: seastar + url: https://api.github.com/repos/ceph/seastar/tarball/0cf6aa6b28d69210b271489c0778f226cde0f459 + md5sum: 3ab003c2f88cb89e0ba9403ec75cd268 + spawn-5f4742f647a5a33b9467f648a3968b3cd0a681ee.tar.gz: + topdir: spawn + url: https://api.github.com/repos/ceph/spawn/tarball/5f4742f647a5a33b9467f648a3968b3cd0a681ee + md5sum: d3aa90d751d53a369482cf8f7e1e9de5 + intel-ipsec-mb-134c90c912ea9376460e9d949bb1319a83a9d839.tar.gz: + topdir: intel-ipsec-mb + url: https://api.github.com/repos/spdk/intel-ipsec-mb/tarball/134c90c912ea9376460e9d949bb1319a83a9d839 + md5sum: 293068c170a663f52f9c713c74fa68f1 + dpdk-a1774652fbbb1fe7c0ff392d5e66de60a0154df6.tar.gz: + topdir: dpdk-a1774652fbbb1fe7c0ff392d5e66de60a0154df6 + url: https://api.github.com/repos/ceph/dpdk/tarball/a1774652fbbb1fe7c0ff392d5e66de60a0154df6 + md5sum: a7460727977b6f093a15e7eb2c122b60 + fmt-80021e25971e44bb6a6d187c0dac8a1823436d80.tar.gz: + topdir: fmt + url: https://api.github.com/repos/ceph/fmt/tarball/80021e25971e44bb6a6d187c0dac8a1823436d80 + md5sum: 6fb91e16e92c5bc59f9074211bce2758 +revision: + dist: $STX_DIST + PKG_GITREVCOUNT: true diff --git a/ceph/ceph/debian/patches/0001-Fix-error-related-to-src-.git_version.patch b/ceph/ceph/debian/patches/0001-Fix-error-related-to-src-.git_version.patch new file mode 100644 index 000000000..ef13923be --- /dev/null +++ b/ceph/ceph/debian/patches/0001-Fix-error-related-to-src-.git_version.patch @@ -0,0 +1,34 @@ +From 234a62418c56cea749e8b86618ea06cbf5cfc2c9 Mon Sep 17 00:00:00 2001 +From: Leonardo Fagundes Luz Serrano + +Date: Tue, 25 Jan 2022 19:55:28 -0300 +Subject: [PATCH] Fix error related to /src/.git_version + +Error message: +"force parsing /<>/src/.git_version +for CEPH_GIT_VER and CEPH_GIT_NICE_VER +CMake Error at src/CMakeLists.txt:183 (file): +file STRINGS file "/<>/src/.git_version" +cannot be read." + +Signed-off-by: Leonardo Fagundes Luz Serrano +--- + src/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index be06fe5016..3c26d1d7f7 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -177,7 +177,7 @@ set(GCOV_PREFIX_STRIP 4) + + # the src/.git_version file may be written out by make-dist; otherwise + # we pull the git version from .git +-option(ENABLE_GIT_VERSION "build Ceph with git version string" ON) ++option(ENABLE_GIT_VERSION "build Ceph with git version string" OFF) + if(${ENABLE_GIT_VERSION}) + message(STATUS "force parsing ${CMAKE_CURRENT_SOURCE_DIR}/.git_version for CEPH_GIT_VER and CEPH_GIT_NICE_VER") + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/.git_version CEPH_GIT_SHA_AND_TAG) +-- +2.17.1 + diff --git a/ceph/ceph/debian/patches/0002-Fix-src-CMakeLists.txts-DESTINATION-error.patch b/ceph/ceph/debian/patches/0002-Fix-src-CMakeLists.txts-DESTINATION-error.patch new file mode 100644 index 000000000..1e6dd487f --- /dev/null +++ b/ceph/ceph/debian/patches/0002-Fix-src-CMakeLists.txts-DESTINATION-error.patch @@ -0,0 +1,166 @@ +From 78afc426e6d37a39706c27305039467b4601cfa5 Mon Sep 17 00:00:00 2001 +From: Leonardo Fagundes Luz Serrano + +Date: Sun, 23 Jan 2022 17:51:26 -0300 +Subject: [PATCH] Fix src/CMakeLists.txts DESTINATION error + +Previous syntax would trigger this error: +"install PROGRAMS given no DESTINATION!" + +Signed-off-by: Leonardo Fagundes Luz Serrano +--- + src/CMakeLists.txt | 124 ++++++++++++++++++++++++++++++--------------- + 1 file changed, 82 insertions(+), 42 deletions(-) + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index be06fe5016..9780ff9511 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -585,40 +585,71 @@ configure_file(ceph-crash.in + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-crash @ONLY) + + if(WITH_TESTS) +- install(PROGRAMS +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-debugpack +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-coverage +- DESTINATION bin) +-endif() +- +-install(PROGRAMS +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-post-file +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-crash +- ${CMAKE_SOURCE_DIR}/src/ceph-run +- ${CMAKE_SOURCE_DIR}/src/ceph-clsinfo +- DESTINATION bin) +-install(PROGRAMS +- ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/init-ceph +- DESTINATION ${CMAKE_INSTALL_INITCEPH} +- PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE +- RENAME ceph) +- +-install(FILES +- ${CMAKE_SOURCE_DIR}/share/id_rsa_drop.ceph.com +- ${CMAKE_SOURCE_DIR}/share/id_rsa_drop.ceph.com.pub +- ${CMAKE_SOURCE_DIR}/share/known_hosts_drop.ceph.com +- DESTINATION ${CMAKE_INSTALL_DATADIR}/ceph) +- +-install(PROGRAMS +- ceph_common.sh +- ceph-osd-prestart.sh +- DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/ceph) +- +-install(PROGRAMS +- ${CMAKE_SOURCE_DIR}/src/ceph-create-keys +-# ${CMAKE_SOURCE_DIR}/src/ceph-disk +- DESTINATION sbin) ++ install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-debugpack ++ DESTINATION bin ++ ) ++ install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-coverage ++ DESTINATION bin ++ ) ++endif() ++ ++install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph ++ DESTINATION bin ++) ++install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-post-file ++ DESTINATION bin ++) ++install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ceph-crash ++ DESTINATION bin ++) ++install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/ceph-run ++ DESTINATION bin ++) ++install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/ceph-clsinfo ++ DESTINATION bin ++) ++ ++install( ++ PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/init-ceph ++ DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/init.d ++ RENAME ceph ++) ++ ++install( ++ FILES ${CMAKE_SOURCE_DIR}/share/id_rsa_drop.ceph.com ++ DESTINATION ${CMAKE_INSTALL_DATADIR}/ceph ++) ++ ++install( ++ FILES ${CMAKE_SOURCE_DIR}/share/id_rsa_drop.ceph.com.pub ++ DESTINATION ${CMAKE_INSTALL_DATADIR}/ceph ++) ++ ++install( ++ FILES ${CMAKE_SOURCE_DIR}/share/known_hosts_drop.ceph.com ++ DESTINATION ${CMAKE_INSTALL_DATADIR}/ceph ++) ++ ++install( ++ PROGRAMS ceph_common.sh ++ DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/ceph ++) ++install( ++ PROGRAMS ceph-osd-prestart.sh ++ DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/ceph ++) ++ ++install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/ceph-create-keys ++ DESTINATION sbin ++) + + add_subdirectory(bash_completion) + add_subdirectory(client) +@@ -683,11 +714,19 @@ if(WITH_RBD) + add_subdirectory(rbd_fuse) + endif() + +- install(PROGRAMS +- ${CMAKE_SOURCE_DIR}/src/ceph-rbdnamer +- ${CMAKE_SOURCE_DIR}/src/rbd-replay-many +- ${CMAKE_SOURCE_DIR}/src/rbdmap +- DESTINATION ${CMAKE_INSTALL_BINDIR}) ++ install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/ceph-rbdnamer ++ DESTINATION ${CMAKE_INSTALL_BINDIR} ++ ) ++ install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/rbd-replay-many ++ DESTINATION ${CMAKE_INSTALL_BINDIR} ++ ) ++ install( ++ PROGRAMS ${CMAKE_SOURCE_DIR}/src/rbdmap ++ DESTINATION ${CMAKE_INSTALL_BINDIR} ++ ) ++ + add_subdirectory(rbd_replay) + endif(WITH_RBD) + +@@ -729,9 +768,10 @@ if(WITH_RADOSGW) + + endif(WITH_RADOSGW) + +-install(FILES +- sample.ceph.conf +- DESTINATION ${CMAKE_INSTALL_DOCDIR}) ++install( ++ FILES sample.ceph.conf ++ DESTINATION ${CMAKE_INSTALL_DOCDIR} ++) + + # Now create a usable config.h + configure_file( +-- +2.17.1 + diff --git a/ceph/ceph/debian/patches/32bit-avoid-overloading.patch b/ceph/ceph/debian/patches/32bit-avoid-overloading.patch new file mode 100644 index 000000000..7d906caff --- /dev/null +++ b/ceph/ceph/debian/patches/32bit-avoid-overloading.patch @@ -0,0 +1,23 @@ +Description: Avoid overloading on 32 bit architectures + unsigned and size_t are equivalent on 32 bit architectures, + so only define the size_t based overload of advance on 64 + bit architectures. + https://wiki.debian.org/ArchitectureSpecificsMemo +Author: James Page , Bernd Zeimetz +Forwarded: no + +--- a/src/include/buffer.h ++++ b/src/include/buffer.h +@@ -737,7 +737,12 @@ inline namespace v14_2_0 { + + void advance(int o) = delete; + void advance(unsigned o); ++ ++// unsigned and size_t are equivalent on 32bit architectures. ++// so casting is only needed when not on 32bit. ++#if defined(UINTPTR_MAX) && UINTPTR_MAX > 0xffffffff + void advance(size_t o) { advance(static_cast(o)); } ++#endif + void seek(unsigned o); + char operator*() const; + iterator_impl& operator++(); diff --git a/ceph/ceph/debian/patches/32bit-avoid-size_t.patch b/ceph/ceph/debian/patches/32bit-avoid-size_t.patch new file mode 100644 index 000000000..1917577c4 --- /dev/null +++ b/ceph/ceph/debian/patches/32bit-avoid-size_t.patch @@ -0,0 +1,112 @@ +Description: Avoid use of size_t when necessary + On 32 bit architectures size_t is not a 64 bit type, which + causes comparison mismatch failures during compilation. +Author: James Page , Bernd Zeimetz +Forwarded: no + +Index: ceph/src/osd/PrimaryLogPG.cc +=================================================================== +--- ceph.orig/src/osd/PrimaryLogPG.cc ++++ ceph/src/osd/PrimaryLogPG.cc +@@ -1611,7 +1611,7 @@ int PrimaryLogPG::do_scrub_ls(MOSDOp *m, + + void PrimaryLogPG::calc_trim_to() + { +- size_t target = cct->_conf->osd_min_pg_log_entries; ++ uint64_t target = cct->_conf->osd_min_pg_log_entries; + if (is_degraded() || + state_test(PG_STATE_RECOVERING | + PG_STATE_RECOVERY_WAIT | +@@ -1627,15 +1627,15 @@ void PrimaryLogPG::calc_trim_to() + if (limit != eversion_t() && + limit != pg_trim_to && + pg_log.get_log().approx_size() > target) { +- size_t num_to_trim = std::min(pg_log.get_log().approx_size() - target, +- cct->_conf->osd_pg_log_trim_max); ++ uint64_t num_to_trim = std::min(pg_log.get_log().approx_size() - target, ++ cct->_conf->osd_pg_log_trim_max); + if (num_to_trim < cct->_conf->osd_pg_log_trim_min && + cct->_conf->osd_pg_log_trim_max >= cct->_conf->osd_pg_log_trim_min) { + return; + } + list::const_iterator it = pg_log.get_log().log.begin(); + eversion_t new_trim_to; +- for (size_t i = 0; i < num_to_trim; ++i) { ++ for (uint64_t i = 0; i < num_to_trim; ++i) { + new_trim_to = it->version; + ++it; + if (new_trim_to > limit) { +Index: ceph/src/os/bluestore/BlueFS.h +=================================================================== +--- ceph.orig/src/os/bluestore/BlueFS.h ++++ ceph/src/os/bluestore/BlueFS.h +@@ -72,7 +72,7 @@ public: + * @params + * alloc_size - allocation unit size to check + */ +- virtual size_t available_freespace(uint64_t alloc_size) = 0; ++ virtual uint64_t available_freespace(uint64_t alloc_size) = 0; + }; + + class BlueFSVolumeSelector { +Index: ceph/src/os/bluestore/BlueStore.cc +=================================================================== +--- ceph.orig/src/os/bluestore/BlueStore.cc ++++ ceph/src/os/bluestore/BlueStore.cc +@@ -5930,12 +5930,12 @@ int BlueStore::allocate_bluefs_freespace + return 0; + } + +-size_t BlueStore::available_freespace(uint64_t alloc_size) { +- size_t total = 0; +- auto iterated_allocation = [&](size_t off, size_t len) { ++uint64_t BlueStore::available_freespace(uint64_t alloc_size) { ++ uint64_t total = 0; ++ auto iterated_allocation = [&](uint64_t off, uint64_t len) { + //only count in size that is alloc_size aligned +- size_t dist_to_alignment; +- size_t offset_in_block = off & (alloc_size - 1); ++ uint64_t dist_to_alignment; ++ uint64_t offset_in_block = off & (alloc_size - 1); + if (offset_in_block == 0) + dist_to_alignment = 0; + else +Index: ceph/src/os/bluestore/BlueStore.h +=================================================================== +--- ceph.orig/src/os/bluestore/BlueStore.h ++++ ceph/src/os/bluestore/BlueStore.h +@@ -3107,7 +3107,7 @@ private: + PExtentVector& extents) override { + return allocate_bluefs_freespace(min_size, size, &extents); + }; +- size_t available_freespace(uint64_t alloc_size) override; ++ uint64_t available_freespace(uint64_t alloc_size) override; + + public: + struct sb_info_t { +Index: ceph/src/common/config_values.h +=================================================================== +--- ceph.orig/src/common/config_values.h ++++ ceph/src/common/config_values.h +@@ -50,7 +50,7 @@ public: + #define OPTION_OPT_U32(name) uint64_t name; + #define OPTION_OPT_U64(name) uint64_t name; + #define OPTION_OPT_UUID(name) uuid_d name; +-#define OPTION_OPT_SIZE(name) size_t name; ++#define OPTION_OPT_SIZE(name) uint64_t name; + #define OPTION(name, ty) \ + public: \ + OPTION_##ty(name) +Index: ceph/src/common/options.cc +=================================================================== +--- ceph.orig/src/common/options.cc ++++ ceph/src/common/options.cc +@@ -189,7 +189,7 @@ int Option::parse_value( + } + *out = uuid; + } else if (type == Option::TYPE_SIZE) { +- Option::size_t sz{strict_iecstrtoll(val.c_str(), error_message)}; ++ Option::size_t sz{static_cast(strict_iecstrtoll(val.c_str(), error_message))}; + if (!error_message->empty()) { + return -EINVAL; + } diff --git a/ceph/ceph/debian/patches/add-option-to-disable-ceph-dencoder.patch b/ceph/ceph/debian/patches/add-option-to-disable-ceph-dencoder.patch new file mode 100644 index 000000000..a9cf20577 --- /dev/null +++ b/ceph/ceph/debian/patches/add-option-to-disable-ceph-dencoder.patch @@ -0,0 +1,11 @@ +Index: ceph/src/tools/CMakeLists.txt +=================================================================== +--- ceph.orig/src/tools/CMakeLists.txt ++++ ceph/src/tools/CMakeLists.txt +@@ -126,4 +126,6 @@ if(WITH_RBD) + endif() + endif(WITH_RBD) + ++if(NOT DISABLE_DENCODER) + add_subdirectory(ceph-dencoder) ++endif(DISABLE_DENCODER) diff --git a/ceph/ceph/debian/patches/allow-bgp-to-host.patch b/ceph/ceph/debian/patches/allow-bgp-to-host.patch new file mode 100644 index 000000000..6c76dda39 --- /dev/null +++ b/ceph/ceph/debian/patches/allow-bgp-to-host.patch @@ -0,0 +1,18 @@ +Description: allow BGP-to-the-host style binding +Author: Thomas Goirand +Forwarded: no +Last-Update: 2021-04-21 + +diff --git a/src/common/ipaddr.cc b/src/common/ipaddr.cc +index 0abf7f20ca..ce81e7e735 100644 +--- a/src/common/ipaddr.cc ++++ b/src/common/ipaddr.cc +@@ -56,7 +56,7 @@ const struct ifaddrs *find_ipv4_in_subnet(const struct ifaddrs *addrs, + if (addrs->ifa_addr == NULL) + continue; + +- if (strcmp(addrs->ifa_name, "lo") == 0 || boost::starts_with(addrs->ifa_name, "lo:")) ++ if (boost::starts_with(addrs->ifa_name, "lo:")) + continue; + + if (numa_node >= 0 && !match_numa_node(addrs->ifa_name, numa_node)) diff --git a/ceph/ceph/debian/patches/another-cmakelists-fix.patch b/ceph/ceph/debian/patches/another-cmakelists-fix.patch new file mode 100644 index 000000000..75abf2339 --- /dev/null +++ b/ceph/ceph/debian/patches/another-cmakelists-fix.patch @@ -0,0 +1,35 @@ +Description: Another cmakelists fix + This fixes the last Boost 1.74 compatibility problems. +Author: Thomas Goirand +Forwarded: no +Last-Update: 2021-01-08 + +Index: ceph/CMakeLists.txt +=================================================================== +--- ceph.orig/CMakeLists.txt ++++ ceph/CMakeLists.txt +@@ -30,6 +30,9 @@ endif() + if(POLICY CMP0093) + cmake_policy(SET CMP0093 NEW) + endif() ++if(POLICY CMP0093) ++ cmake_policy(SET CMP0093 NEW) ++endif() + list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/") + + if(CMAKE_SYSTEM_NAME MATCHES "Linux") +Index: ceph/src/rgw/CMakeLists.txt +=================================================================== +--- ceph.orig/src/rgw/CMakeLists.txt ++++ ceph/src/rgw/CMakeLists.txt +@@ -23,6 +23,10 @@ if(Boost_VERSION VERSION_GREATER 1.73) + add_definitions(-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) + endif() + ++if(Boost_VERSION VERSION_GREATER_EQUAL 1.74) ++ add_definitions(-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) ++endif() ++ + set(librgw_common_srcs + services/svc_finisher.cc + services/svc_notify.cc diff --git a/ceph/ceph/debian/patches/bluefs-use-uint64_t-for-len.patch b/ceph/ceph/debian/patches/bluefs-use-uint64_t-for-len.patch new file mode 100644 index 000000000..fb629b88a --- /dev/null +++ b/ceph/ceph/debian/patches/bluefs-use-uint64_t-for-len.patch @@ -0,0 +1,58 @@ +From 10a953afc8f803e50c96354470fb114b33e62599 Mon Sep 17 00:00:00 2001 +From: Kefu Chai +Date: Fri, 28 Jun 2019 11:35:54 +0800 +Subject: [PATCH] os/bluestore/BlueFS: use uint64_t for `len` + +change the type of parameter `len` of `BlueFS::_read_random()` from +`size_t` to `uint64_t`. + +i think the type of `size_t` comes from +`rocksdb::RandomAccessFile::Read(uint64_t offset, size_t n, +rocksdb::Slice* result, char* scratch)`. and when we implement this +method, we continued using `n`'s type. but, we are using it with +`std::min()`, for instance, where the template parameter type deduction +fails if the lhs and rhs parameters' types are different. so probaly the +better solution is to use `uint64_t` directly to avoid the the cast and +specializing the template. + +Signed-off-by: Kefu Chai +--- + src/os/bluestore/BlueFS.cc | 4 ++-- + src/os/bluestore/BlueFS.h | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +Index: ceph/src/os/bluestore/BlueFS.cc +=================================================================== +--- ceph.orig/src/os/bluestore/BlueFS.cc ++++ ceph/src/os/bluestore/BlueFS.cc +@@ -1678,7 +1678,7 @@ void BlueFS::_drop_link(FileRef file) + int64_t BlueFS::_read_random( + FileReader *h, ///< [in] read from here + uint64_t off, ///< [in] offset +- size_t len, ///< [in] this many bytes ++ uint64_t len, ///< [in] this many bytes + char *out) ///< [out] optional: or copy it here + { + auto* buf = &h->buf; +@@ -1710,7 +1710,7 @@ int64_t BlueFS::_read_random( + uint64_t x_off = 0; + auto p = h->file->fnode.seek(off, &x_off); + ceph_assert(p != h->file->fnode.extents.end()); +- uint64_t l = std::min(p->length - x_off, static_cast(len)); ++ uint64_t l = std::min(p->length - x_off, len); + //hard cap to 1GB + l = std::min(l, uint64_t(1) << 30); + dout(20) << __func__ << " read random 0x" +Index: ceph/src/os/bluestore/BlueFS.h +=================================================================== +--- ceph.orig/src/os/bluestore/BlueFS.h ++++ ceph/src/os/bluestore/BlueFS.h +@@ -422,7 +422,7 @@ private: + int64_t _read_random( + FileReader *h, ///< [in] read from here + uint64_t offset, ///< [in] offset +- size_t len, ///< [in] this many bytes ++ uint64_t len, ///< [in] this many bytes + char *out); ///< [out] optional: or copy it here + + void _invalidate_cache(FileRef f, uint64_t offset, uint64_t length); diff --git a/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable.patch b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable.patch new file mode 100644 index 000000000..8313b3ece --- /dev/null +++ b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable.patch @@ -0,0 +1,53 @@ +Description: Makes SOMAXCONN user-configurable. +Author: Jesse Williamson +Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/febab7dc38c9671577603425c54c20f841e27f97 +Bug: https://github.com/civetweb/civetweb/issues/775 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/src/civetweb/src/civetweb.c ++++ b/src/civetweb/src/civetweb.c +@@ -1541,10 +1541,6 @@ typedef int socklen_t; + #define MSG_NOSIGNAL (0) + #endif + +-#if !defined(SOMAXCONN) +-#define SOMAXCONN (100) +-#endif +- + /* Size of the accepted socket queue */ + #if !defined(MGSQLEN) + #define MGSQLEN (20) +@@ -2063,6 +2059,7 @@ enum { + SSL_CERTIFICATE, + SSL_CERTIFICATE_CHAIN, + NUM_THREADS, ++ SO_MAX_CONNECTIONS, + RUN_AS_USER, + URL_REWRITE_PATTERN, + HIDE_FILES, +@@ -2165,6 +2162,7 @@ static struct mg_option config_options[] + {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, + {"ssl_certificate_chain", CONFIG_TYPE_FILE, NULL}, + {"num_threads", CONFIG_TYPE_NUMBER, "50"}, ++ {"max_connections", CONFIG_TYPE_NUMBER, "100"}, + {"run_as_user", CONFIG_TYPE_STRING, NULL}, + {"url_rewrite_patterns", CONFIG_TYPE_STRING_LIST, NULL}, + {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, +@@ -13340,7 +13338,15 @@ set_ports_option(struct mg_context *ctx) + continue; + } + +- if (listen(so.sock, SOMAXCONN) != 0) { ++ char *p = ctx->config[SO_MAX_CONNECTIONS]; ++ long opt_max_connections = strtol(p, NULL, 10); ++ if(opt_max_connections > INT_MAX || opt_max_connections < 1) { ++ mg_cry(fc(ctx), ++ "max_connections value \"%s\" is invalid", p); ++ continue; ++ } ++ ++ if (listen(so.sock, (int)opt_max_connections) != 0) { + + mg_cry(fc(ctx), + "cannot listen to %.*s: %d (%s)", diff --git a/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch new file mode 100644 index 000000000..e8e3b0b0f --- /dev/null +++ b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch @@ -0,0 +1,18 @@ +Description: Adds max_connections to reference configuration. +Author: Jesse Williamson +Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/3b8eb36676f70d06f8918ccf62029207c49cdda0 +Bug: https://github.com/civetweb/civetweb/issues/775 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/src/civetweb/resources/civetweb.conf ++++ b/src/civetweb/resources/civetweb.conf +@@ -8,6 +8,8 @@ + document_root . + listening_ports 8080 + ++#so_max_connections 100 ++ + # cgi_pattern **.cgi$|**.pl$|**.php$ + # cgi_environment + # put_delete_auth_file diff --git a/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch new file mode 100644 index 000000000..cdd27b5ab --- /dev/null +++ b/ceph/ceph/debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch @@ -0,0 +1,17 @@ +Description: Adds max_connections to test display. +Author: Jesse Williamson +Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/3b8eb36676f70d06f8918ccf62029207c49cdda0 +Bug: https://github.com/civetweb/civetweb/issues/775 +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/src/civetweb/test/page3.ssjs ++++ b/src/civetweb/test/page3.ssjs +@@ -21,6 +21,7 @@ opts = [ + "document_root", + "ssl_certificate", + "num_threads", ++"max_connections", + "run_as_user", + "url_rewrite_patterns", + "hide_files_patterns", diff --git a/ceph/ceph/debian/patches/cmake_add_1.74_to_known_versions.patch b/ceph/ceph/debian/patches/cmake_add_1.74_to_known_versions.patch new file mode 100644 index 000000000..004788918 --- /dev/null +++ b/ceph/ceph/debian/patches/cmake_add_1.74_to_known_versions.patch @@ -0,0 +1,52 @@ +Description: cmake: add 1.74 to known versions +Author: Kefu Chai +Bug-Debian: https://bugs.debian.org/977243 +Origin: upstream, https://github.com/ceph/ceph/commit/b6a94da6149e50bdd43752919d7c01b04c59f79e.patch +Last-Update: 2020-12-13 + +--- ceph-14.2.15.orig/cmake/modules/FindBoost.cmake ++++ ceph-14.2.15/cmake/modules/FindBoost.cmake +@@ -437,10 +437,23 @@ if (NOT Boost_NO_BOOST_CMAKE) + endif() + endif() + ++ set(_boost_FIND_PACKAGE_ARGS "") ++ if(Boost_NO_SYSTEM_PATHS) ++ list(APPEND _boost_FIND_PACKAGE_ARGS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) ++ endif() ++ + # Do the same find_package call but look specifically for the CMake version. + # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no + # need to delegate them to this find_package call. +- find_package(Boost QUIET NO_MODULE) ++ cmake_policy(PUSH) ++ if(BOOST_ROOT AND NOT Boost_ROOT) ++ if(POLICY CMP0074) ++ cmake_policy(SET CMP0074 NEW) ++ endif() ++ set(Boost_ROOT "${BOOST_ROOT}") ++ endif() ++ find_package(Boost QUIET NO_MODULE ${_boost_FIND_PACKAGE_ARGS}) ++ cmake_policy(POP) + mark_as_advanced(Boost_DIR) + + # If we found a boost cmake package, then we're done. Print out what we found. +@@ -1157,7 +1170,7 @@ function(_Boost_COMPONENT_DEPENDENCIES c + set(_Boost_TIMER_DEPENDENCIES chrono) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) +- if(NOT Boost_VERSION_STRING VERSION_LESS 1.73.0) ++ if(NOT Boost_VERSION_STRING VERSION_LESS 1.75.0) + message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") + endif() + endif() +@@ -1429,7 +1442,8 @@ else() + # _Boost_COMPONENT_HEADERS. See the instructions at the top of + # _Boost_COMPONENT_DEPENDENCIES. + set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} +- "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" ++ "1.74.0" "1.74" ++ "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" + "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" + "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" + "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" diff --git a/ceph/ceph/debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch b/ceph/ceph/debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch new file mode 100644 index 000000000..2c792ce67 --- /dev/null +++ b/ceph/ceph/debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch @@ -0,0 +1,30 @@ +Description: cmake: define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT for + Boost.Asio users + . + see also + https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/std_executors.html#boost_asio.std_executors.polymorphic_i_o_executor + . + we could use `asio::any_io_executor` later on though for better + performance. + . + also, define CMP0093, so FindBoost reports Boost_VERSION in x.y.z + format. it is simpler to use `VERSION_GREATER_EQUAL` to compare its + version with 1.74 instead of its C macro version ("107000"). +Signed-off-by: Kefu Chai +Author: Kefu Chai +Origin: upstream, https://github.com/ceph/ceph/commit/3d708219092d0e89a1434c30ffc8a4999f062cc0.patch +Bug-Debian: https://bugs.debian.org/977243 +Last-Update: 2021-03-24 + +--- ceph-14.2.15.orig/CMakeLists.txt 2020-12-14 09:42:50.543215302 +0100 ++++ ceph-14.2.15/CMakeLists.txt 2020-12-14 09:44:07.827084724 +0100 +@@ -21,6 +21,9 @@ + if(POLICY CMP0051) + cmake_policy(SET CMP0051 NEW) + endif() ++if(POLICY CMP0074) ++ cmake_policy(SET CMP0074 NEW) ++endif() + if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) + endif() diff --git a/ceph/ceph/debian/patches/debian-armel-armhf-buildflags.patch b/ceph/ceph/debian/patches/debian-armel-armhf-buildflags.patch new file mode 100644 index 000000000..e9a450aa5 --- /dev/null +++ b/ceph/ceph/debian/patches/debian-armel-armhf-buildflags.patch @@ -0,0 +1,45 @@ +--- a/cmake/modules/SIMDExt.cmake ++++ b/cmake/modules/SIMDExt.cmake +@@ -40,11 +40,14 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch + + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM") + set(HAVE_ARM 1) +- CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_ARM_NEON) +- if(HAVE_ARM_NEON) +- set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfpu=neon") ++ if(CMAKE_LIBRARY_ARCHITECTURE EQUAL "arm-linux-gnueabi") ++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} --with-arch=armv5te --with-float=soft") ++ else() ++ CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_ARM_NEON) ++ if(HAVE_ARM_NEON) ++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfpu=neon") ++ endif() + endif() +- + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686|amd64|x86_64|AMD64") + set(HAVE_INTEL 1) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686|amd64|x86_64|AMD64") +--- a/src/erasure-code/jerasure/gf-complete/m4/ax_ext.m4 ++++ b/src/erasure-code/jerasure/gf-complete/m4/ax_ext.m4 +@@ -19,10 +19,17 @@ AC_DEFUN([AX_EXT], + ;; + + arm*) +- AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) +- if test "$ax_cv_have_neon_ext" = yes; then +- AX_CHECK_COMPILE_FLAG(-mfpu=neon, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=neon -DARM_NEON"], [ax_cv_have_neon_ext=no]) +- fi ++ case $host_cpu in ++ arm-linux-gnueabi) ++ AX_CHECK_COMPILE_FLAG(-mfpu=soft, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=soft -march=armv5te"], [ax_cv_have_neon_ext=no]) ++ ;; ++ *) ++ AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) ++ if test "$ax_cv_have_neon_ext" = yes; then ++ AX_CHECK_COMPILE_FLAG(-mfpu=neon, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=neon -DARM_NEON"], [ax_cv_have_neon_ext=no]) ++ fi ++ ;; ++ esac + ;; + + powerpc*) diff --git a/ceph/ceph/debian/patches/disable-crypto.patch b/ceph/ceph/debian/patches/disable-crypto.patch new file mode 100644 index 000000000..7cd126829 --- /dev/null +++ b/ceph/ceph/debian/patches/disable-crypto.patch @@ -0,0 +1,16 @@ +Index: ceph/src/os/CMakeLists.txt +=================================================================== +--- ceph.orig/src/os/CMakeLists.txt ++++ ceph/src/os/CMakeLists.txt +@@ -110,8 +110,9 @@ endif() + target_link_libraries(os kv) + + add_dependencies(os compressor_plugins) +-add_dependencies(os crypto_plugins) +- ++if(HAVE_INTEL AND HAVE_BETTER_YASM_ELF64 AND (NOT APPLE)) ++ add_dependencies(os crypto_plugins) ++endif() + + if(WITH_BLUESTORE) + add_executable(ceph-bluestore-tool diff --git a/ceph/ceph/debian/patches/fix-bash-completion-location b/ceph/ceph/debian/patches/fix-bash-completion-location new file mode 100644 index 000000000..916ff8a79 --- /dev/null +++ b/ceph/ceph/debian/patches/fix-bash-completion-location @@ -0,0 +1,9 @@ +--- a/src/bash_completion/CMakeLists.txt ++++ b/src/bash_completion/CMakeLists.txt +@@ -11,5 +11,5 @@ if(WITH_RADOSGW) + endif() + + install(FILES ${completions} +- DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d) ++ DESTINATION /usr/share/bash-completion/completions) + diff --git a/ceph/ceph/debian/patches/fix-ceph-osd-systemd-target.patch b/ceph/ceph/debian/patches/fix-ceph-osd-systemd-target.patch new file mode 100644 index 000000000..9f42077ba --- /dev/null +++ b/ceph/ceph/debian/patches/fix-ceph-osd-systemd-target.patch @@ -0,0 +1,17 @@ +Description: Fix systemd ceph-osd.target + This helps when rebooting. +Author: Thomas Goirand +Forwarded: no +Last-Update: 2021-01-28 + +--- ceph-14.2.16.orig/systemd/ceph-osd.target ++++ ceph-14.2.16/systemd/ceph-osd.target +@@ -1,7 +1,7 @@ + [Unit] + Description=ceph target allowing to start/stop all ceph-osd@.service instances at once + PartOf=ceph.target +-After=ceph-mon.target ++After=ceph-mon.target systemd-udev-settle.service + Before=ceph.target + Wants=ceph.target ceph-mon.target + diff --git a/ceph/ceph/debian/patches/make-ceph-python-3.9-aware.patch b/ceph/ceph/debian/patches/make-ceph-python-3.9-aware.patch new file mode 100644 index 000000000..67e55ea7b --- /dev/null +++ b/ceph/ceph/debian/patches/make-ceph-python-3.9-aware.patch @@ -0,0 +1,28 @@ +Description: Make Ceph Python 3.9 aware + Add versions of interpreters Ceph didn't know about. +Author: Thomas Goirand +Forwarded: no +Last-Update: 2020-11-28 + +--- ceph-14.2.15.orig/cmake/modules/FindPython3Interp.cmake ++++ ceph-14.2.15/cmake/modules/FindPython3Interp.cmake +@@ -69,7 +69,7 @@ + + unset(_Python3_NAMES) + +-set(_PYTHON3_VERSIONS 3.6 3.5 3.4 3.3 3.2 3.1 3.0) ++set(_PYTHON3_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + + if(Python3Interp_FIND_VERSION) + if(Python3Interp_FIND_VERSION_COUNT GREATER 1) +--- ceph-14.2.15.orig/cmake/modules/FindPython3Libs.cmake ++++ ceph-14.2.15/cmake/modules/FindPython3Libs.cmake +@@ -101,7 +101,7 @@ endif() + # To avoid picking up the system Python.h pre-maturely. + set(CMAKE_FIND_FRAMEWORK LAST) + +-set(_PYTHON3_VERSIONS 3.6 3.5 3.4 3.3 3.2 3.1 3.0) ++set(_PYTHON3_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + + if(Python3Libs_FIND_VERSION) + if(Python3Libs_FIND_VERSION_COUNT GREATER 1) diff --git a/ceph/ceph/debian/patches/mds-purgequeue-use_uint64_t.patch b/ceph/ceph/debian/patches/mds-purgequeue-use_uint64_t.patch new file mode 100644 index 000000000..8797da213 --- /dev/null +++ b/ceph/ceph/debian/patches/mds-purgequeue-use_uint64_t.patch @@ -0,0 +1,31 @@ +Index: ceph/src/mds/PurgeQueue.cc +=================================================================== +--- ceph.orig/src/mds/PurgeQueue.cc ++++ ceph/src/mds/PurgeQueue.cc +@@ -499,7 +499,7 @@ void PurgeQueue::_execute_item( + + in_flight[expire_to] = item; + logger->set(l_pq_executing, in_flight.size()); +- files_high_water = std::max(files_high_water, in_flight.size()); ++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); + logger->set(l_pq_executing_high_water, files_high_water); + auto ops = _calculate_ops(item); + ops_in_flight += ops; +@@ -577,7 +577,7 @@ void PurgeQueue::_execute_item( + logger->set(l_pq_executing_ops_high_water, ops_high_water); + in_flight.erase(expire_to); + logger->set(l_pq_executing, in_flight.size()); +- files_high_water = std::max(files_high_water, in_flight.size()); ++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); + logger->set(l_pq_executing_high_water, files_high_water); + return; + } +@@ -654,7 +654,7 @@ void PurgeQueue::_execute_item_complete( + + in_flight.erase(iter); + logger->set(l_pq_executing, in_flight.size()); +- files_high_water = std::max(files_high_water, in_flight.size()); ++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); + logger->set(l_pq_executing_high_water, files_high_water); + dout(10) << "in_flight.size() now " << in_flight.size() << dendl; + diff --git a/ceph/ceph/debian/patches/riscv64-link-pthread.patch b/ceph/ceph/debian/patches/riscv64-link-pthread.patch new file mode 100644 index 000000000..5d4a72d49 --- /dev/null +++ b/ceph/ceph/debian/patches/riscv64-link-pthread.patch @@ -0,0 +1,16 @@ +Description: Link with -pthread instead of -lpthread to fix FTBFS on riscv64 +Forwarded: no +Last-Update: 2020-03-01 + +Index: ceph/CMakeLists.txt +=================================================================== +--- ceph.orig/CMakeLists.txt ++++ ceph/CMakeLists.txt +@@ -31,6 +31,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_S + + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(LINUX ON) ++ set(THREADS_PREFER_PTHREAD_FLAG ON) + FIND_PACKAGE(Threads) + elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(FREEBSD ON) diff --git a/ceph/ceph/debian/patches/series b/ceph/ceph/debian/patches/series new file mode 100644 index 000000000..e4d6aec6f --- /dev/null +++ b/ceph/ceph/debian/patches/series @@ -0,0 +1,24 @@ +update-java-source-target-flags.patch +disable-crypto.patch +32bit-avoid-overloading.patch +32bit-avoid-size_t.patch +# Ubuntu: civetweb max connections +civetweb-755-1.8-somaxconn-configurable_conf.patch +civetweb-755-1.8-somaxconn-configurable.patch +civetweb-755-1.8-somaxconn-configurable_test.patch +# Upstream: py3 +# Upstream: 32bit +bluefs-use-uint64_t-for-len.patch +debian-armel-armhf-buildflags.patch +fix-bash-completion-location +add-option-to-disable-ceph-dencoder.patch +riscv64-link-pthread.patch +mds-purgequeue-use_uint64_t.patch +make-ceph-python-3.9-aware.patch +cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch +cmake_add_1.74_to_known_versions.patch +another-cmakelists-fix.patch +fix-ceph-osd-systemd-target.patch +# allow-bgp-to-host.patch ## Not applicable to stx/14.2.22 version +0001-Fix-error-related-to-src-.git_version.patch +0002-Fix-src-CMakeLists.txts-DESTINATION-error.patch diff --git a/ceph/ceph/debian/patches/update-java-source-target-flags.patch b/ceph/ceph/debian/patches/update-java-source-target-flags.patch new file mode 100644 index 000000000..6c8b79228 --- /dev/null +++ b/ceph/ceph/debian/patches/update-java-source-target-flags.patch @@ -0,0 +1,23 @@ +Description: use --release 7 instead of -source/-target + Instead of -source/-target ceph should be build with --release for OpenJDK 9 + or later so that the bootclasspath is also set, as per JEP-247, otherwise it + risks incurring into binary incompatibility when run with an earlier OpenJDK. + OpenJDK 11 minimum compatibility release has been updated to 7. +Author: Tiago Stürmer Daitx +Bug-Ubuntu: https://launchpad.net/bugs/1756854 +Bug-Ubuntu: https://launchpad.net/bugs/1766998 +Forwarded: no +Last-Update: 2018-04-24 +--- + +--- a/src/java/CMakeLists.txt ++++ b/src/java/CMakeLists.txt +@@ -21,7 +21,7 @@ set(java_srcs + # warning: [options] bootstrap class path not set in conjunction with -source 1.7 + # as per + # https://blogs.oracle.com/darcy/entry/bootclasspath_older_source +-set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.8" "-target" "1.8" "-Xlint:-options") ++set(CMAKE_JAVA_COMPILE_FLAGS "--release" "7" "-Xlint:-options") + set(jni_header_dir "${CMAKE_CURRENT_BINARY_DIR}/native") + if(CMAKE_VERSION VERSION_LESS 3.11) + set(CMAKE_JAVA_COMPILE_FLAGS ${CMAKE_JAVA_COMPILE_FLAGS} "-h" ${jni_header_dir})