Merge branch 'master' of https://github.com/highfidelity/hifi into ambient

This commit is contained in:
samcake 2017-05-03 14:54:11 -07:00
commit 26441dbf2f
39 changed files with 982 additions and 545 deletions

View file

@ -2526,6 +2526,11 @@ bool Application::event(QEvent* event) {
isPaintingThrottled = false;
return true;
} else if ((int)event->type() == (int)Idle) {
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
idle(nsecsElapsed);
return true;
}
@ -6491,10 +6496,22 @@ void Application::activeChanged(Qt::ApplicationState state) {
}
void Application::windowMinimizedChanged(bool minimized) {
// initialize the _minimizedWindowTimer
static std::once_flag once;
std::call_once(once, [&] {
connect(&_minimizedWindowTimer, &QTimer::timeout, this, [] {
QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);
});
});
// avoid rendering to the display plugin but continue posting Idle events,
// so that physics continues to simulate and the deadlock watchdog knows we're alive
if (!minimized && !getActiveDisplayPlugin()->isActive()) {
_minimizedWindowTimer.stop();
getActiveDisplayPlugin()->activate();
} else if (minimized && getActiveDisplayPlugin()->isActive()) {
getActiveDisplayPlugin()->deactivate();
_minimizedWindowTimer.start(THROTTLED_SIM_FRAME_PERIOD_MS);
}
}

View file

@ -138,6 +138,7 @@ public:
enum Event {
Present = DisplayPlugin::Present,
Paint = Present + 1,
Idle = Paint + 1,
Lambda = Paint + 1
};
@ -536,6 +537,7 @@ private:
RateCounter<> _avatarSimCounter;
RateCounter<> _simCounter;
QTimer _minimizedWindowTimer;
QElapsedTimer _timerStart;
QElapsedTimer _lastTimeUpdated;

View file

@ -31,6 +31,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
auto dataObject = doc.object().value("data").toObject();
QString thumbnailUrl = dataObject.value("thumbnail_url").toString();
QString imageUrl = dataObject.value("image_url").toString();
QString snapshotID = dataObject.value("id").toString();
auto addressManager = DependencyManager::get<AddressManager>();
QString placeName = _inWorldLocation.authority(); // We currently only upload shareable places, in which case this is just host.
QString currentPath = _inWorldLocation.path();
@ -43,6 +44,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) {
if (dataObject.contains("shareable_url")) {
detailsObject.insert("shareable_url", dataObject.value("shareable_url").toString());
}
detailsObject.insert("snapshot_id", snapshotID);
QString pickledDetails = QJsonDocument(detailsObject).toJson();
userStoryObject.insert("details", pickledDetails);
userStoryObject.insert("thumbnail_url", thumbnailUrl);

View file

@ -18,6 +18,8 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail)
#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes))
namespace gpu { namespace gl {
// Create a fence and inject a GPU wait on the fence

View file

@ -11,6 +11,20 @@
using namespace gpu;
using namespace gpu::gl;
bool GLTexelFormat::isCompressed() const {
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
return true;
break;
default:
return false;
break;
}
}
GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
GLenum result = GL_RGBA8;

View file

@ -18,6 +18,11 @@ public:
GLenum format;
GLenum type;
GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {}
GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {}
bool isCompressed() const;
static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) {
return evalGLTexelFormat(dstFormat, dstFormat);
}

View file

@ -102,7 +102,8 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :
GLObject(backend, texture, id),
_source(texture.source()),
_target(getGLTextureType(texture))
_target(getGLTextureType(texture)),
_texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat()))
{
Backend::setGPUObject(texture, this);
}
@ -150,6 +151,7 @@ GLExternalTexture::~GLExternalTexture() {
// Variable sized textures
using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState;
using WorkQueue = GLVariableAllocationSupport::WorkQueue;
using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer;
std::list<TextureWeakPointer> GLVariableAllocationSupport::_memoryManagedTextures;
MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle };
@ -159,6 +161,7 @@ WorkQueue GLVariableAllocationSupport::_transferQueue;
WorkQueue GLVariableAllocationSupport::_promoteQueue;
WorkQueue GLVariableAllocationSupport::_demoteQueue;
TexturePointer GLVariableAllocationSupport::_currentTransferTexture;
TransferJobPointer GLVariableAllocationSupport::_currentTransferJob;
size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 };
#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f
@ -553,9 +556,15 @@ void GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& curr
if (!_pendingTransfers.empty()) {
// Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture
_currentTransferTexture = currentTexture;
if (_pendingTransfers.front()->tryTransfer()) {
// Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job
// doesn't leave scope, causing a crash in the buffering thread
_currentTransferJob = _pendingTransfers.front();
// transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering
// is complete
if (_currentTransferJob->tryTransfer()) {
_pendingTransfers.pop();
_currentTransferTexture.reset();
_currentTransferJob.reset();
}
}
}

View file

@ -86,7 +86,8 @@ public:
void transfer();
};
using TransferQueue = std::queue<std::unique_ptr<TransferJob>>;
using TransferJobPointer = std::shared_ptr<TransferJob>;
using TransferQueue = std::queue<TransferJobPointer>;
static MemoryPressureState _memoryPressureState;
public:
@ -100,6 +101,7 @@ protected:
static WorkQueue _promoteQueue;
static WorkQueue _demoteQueue;
static TexturePointer _currentTransferTexture;
static TransferJobPointer _currentTransferJob;
static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS;
static const uvec3 MAX_TRANSFER_DIMENSIONS;
static const size_t MAX_TRANSFER_SIZE;
@ -153,6 +155,7 @@ public:
const GLuint& _texture { _id };
const std::string _source;
const GLenum _target;
GLTexelFormat _texelFormat;
static const std::vector<GLenum>& getFaceTargets(GLenum textureType);
static uint8_t getFaceCount(GLenum textureType);

View file

@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(gpugl41logging, "hifi.gpu.gl41")
using namespace gpu;
using namespace gpu::gl41;
const std::string GL41Backend::GL41_VERSION { "GL41" };
void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) {
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];

View file

@ -37,12 +37,16 @@ class GL41Backend : public GLBackend {
public:
static const GLint TRANSFORM_OBJECT_SLOT { 31 };
static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 };
static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 33 };
static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 34 };
static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 };
static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 };
static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 };
explicit GL41Backend(bool syncCache) : Parent(syncCache) {}
GL41Backend() : Parent() {}
static const std::string GL41_VERSION;
const std::string& getVersion() const override { return GL41_VERSION; }
class GL41Texture : public GLTexture {
using Parent = GLTexture;
friend class GL41Backend;

View file

@ -240,7 +240,9 @@ GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr<GLBacke
using GL41VariableAllocationTexture = GL41Backend::GL41VariableAllocationTexture;
GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL41Texture(backend, texture) {
GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) :
GL41Texture(backend, texture)
{
auto mipLevels = texture.getNumMips();
_allocatedMip = mipLevels;
_maxAllocatedMip = _populatedMip = mipLevels;
@ -306,6 +308,129 @@ void GL41VariableAllocationTexture::syncSampler() const {
});
}
void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT
GLuint fbo { 0 };
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = numMips;
// copy pre-existing mips
for (uint16_t mip = populatedMips; mip < mips; ++mip) {
auto mipDimensions = texture.evalMipDimensions(mip);
uint16_t targetMip = mip - destMipOffset;
uint16_t sourceMip = mip - srcMipOffset;
for (GLenum target : GLTexture::getFaceTargets(texTarget)) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip);
(void)CHECK_GL_ERROR();
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
(void)CHECK_GL_ERROR();
}
}
// destroy the transfer framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
}
void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
// DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT
struct MipDesc {
GLint _faceSize;
GLint _size;
GLint _offset;
GLint _width;
GLint _height;
};
std::vector<MipDesc> sourceMips(numMips);
std::vector<GLubyte> bytes;
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT);
glBindTexture(texTarget, srcId);
const auto& faceTargets = GLTexture::getFaceTargets(texTarget);
GLint internalFormat { 0 };
// Collect the mip description from the source texture
GLint bufferOffset { 0 };
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
// Grab internal format once
if (internalFormat == 0) {
glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
}
// Collect the size of the first face, and then compute the total size offset needed for this mip level
auto mipDimensions = texture.evalMipDimensions(mip);
sourceMip._width = mipDimensions.x;
sourceMip._height = mipDimensions.y;
#ifdef DEBUG_COPY
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width);
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height);
#endif
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize);
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
sourceMip._offset = bufferOffset;
bufferOffset += sourceMip._size;
gpu::gl::checkGLError();
}
(void)CHECK_GL_ERROR();
// Allocate the PBO to accomodate for all the mips to copy
GLuint pbo { 0 };
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY);
(void)CHECK_GL_ERROR();
// Transfer from source texture to pbo
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t sourceLevel = mip - srcMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
}
(void)CHECK_GL_ERROR();
}
// Now populate the new texture from the pbo
glBindTexture(texTarget, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT);
// Transfer from pbo to new texture
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto& sourceMip = sourceMips[mip];
uint16_t destLevel = mip - destMipOffset;
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
#ifdef DEBUG_COPY
GLint destWidth, destHeight, destSize;
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight);
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize);
#endif
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
gpu::gl::checkGLError();
}
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
}
void GL41VariableAllocationTexture::promote() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Q_ASSERT(_allocatedMip > 0);
@ -315,36 +440,22 @@ void GL41VariableAllocationTexture::promote() {
GLuint oldId = _id;
auto oldSize = _size;
uint16_t oldAllocatedMip = _allocatedMip;
// create new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
uint16_t oldAllocatedMip = _allocatedMip;
// allocate storage for new level
allocateStorage(targetAllocatedMip);
// copy pre-existing mips
uint16_t numMips = _gpuObject.getNumMips();
withPreservedTexture([&] {
GLuint fbo { 0 };
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = _gpuObject.getNumMips();
// copy pre-existing mips
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
uint16_t targetMip = mip - _allocatedMip;
uint16_t sourceMip = mip - oldAllocatedMip;
for (GLenum target : getFaceTargets(_target)) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip);
(void)CHECK_GL_ERROR();
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
(void)CHECK_GL_ERROR();
}
if (_texelFormat.isCompressed()) {
copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
} else {
copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
}
// destroy the transfer framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
syncSampler();
});
@ -360,34 +471,21 @@ void GL41VariableAllocationTexture::demote() {
Q_ASSERT(_allocatedMip < _maxAllocatedMip);
auto oldId = _id;
auto oldSize = _size;
// allocate new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
uint16_t oldAllocatedMip = _allocatedMip;
allocateStorage(_allocatedMip + 1);
_populatedMip = std::max(_populatedMip, _allocatedMip);
// copy pre-existing mips
uint16_t numMips = _gpuObject.getNumMips();
withPreservedTexture([&] {
GLuint fbo { 0 };
glCreateFramebuffers(1, &fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
uint16_t mips = _gpuObject.getNumMips();
// copy pre-existing mips
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
uint16_t targetMip = mip - _allocatedMip;
uint16_t sourceMip = mip - oldAllocatedMip;
for (GLenum target : getFaceTargets(_target)) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip);
(void)CHECK_GL_ERROR();
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
(void)CHECK_GL_ERROR();
}
if (_texelFormat.isCompressed()) {
copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
} else {
copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
}
// destroy the transfer framebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
syncSampler();
});
@ -463,4 +561,3 @@ GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr<GLBackend>& backend
GL41ResourceTexture::~GL41ResourceTexture() {
}

View file

@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45")
using namespace gpu;
using namespace gpu::gl45;
const std::string GL45Backend::GL45_VERSION { "GL45" };
void GL45Backend::recycle() const {
Parent::recycle();
}

View file

@ -41,6 +41,9 @@ public:
explicit GL45Backend(bool syncCache) : Parent(syncCache) {}
GL45Backend() : Parent() {}
static const std::string GL45_VERSION;
const std::string& getVersion() const override { return GL45_VERSION; }
class GL45Texture : public GLTexture {
using Parent = GLTexture;
friend class GL45Backend;

View file

@ -97,6 +97,24 @@ void GL45ResourceTexture::syncSampler() const {
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
}
void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
auto mipDimensions = texture.evalMipDimensions(mip);
uint16_t targetMip = mip - destMipOffset;
uint16_t sourceMip = mip - srcMipOffset;
auto faces = GLTexture::getFaceCount(texTarget);
for (uint8_t face = 0; face < faces; ++face) {
glCopyImageSubData(
srcId, texTarget, sourceMip, 0, 0, face,
destId, texTarget, targetMip, 0, 0, face,
mipDimensions.x, mipDimensions.y, 1
);
(void)CHECK_GL_ERROR();
}
}
}
void GL45ResourceTexture::promote() {
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
Q_ASSERT(_allocatedMip > 0);
@ -106,27 +124,18 @@ void GL45ResourceTexture::promote() {
GLuint oldId = _id;
auto oldSize = _size;
uint16_t oldAllocatedMip = _allocatedMip;
// create new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
uint16_t oldAllocatedMip = _allocatedMip;
// allocate storage for new level
allocateStorage(targetAllocatedMip);
uint16_t mips = _gpuObject.getNumMips();
// copy pre-existing mips
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
uint16_t targetMip = mip - _allocatedMip;
uint16_t sourceMip = mip - oldAllocatedMip;
auto faces = getFaceCount(_target);
for (uint8_t face = 0; face < faces; ++face) {
glCopyImageSubData(
oldId, _target, sourceMip, 0, 0, face,
_id, _target, targetMip, 0, 0, face,
mipDimensions.x, mipDimensions.y, 1
);
(void)CHECK_GL_ERROR();
}
}
uint16_t numMips = _gpuObject.getNumMips();
copyTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
// destroy the old texture
glDeleteTextures(1, &oldId);
// update the memory usage
@ -140,25 +149,17 @@ void GL45ResourceTexture::demote() {
Q_ASSERT(_allocatedMip < _maxAllocatedMip);
auto oldId = _id;
auto oldSize = _size;
// allocate new texture
const_cast<GLuint&>(_id) = allocate(_gpuObject);
uint16_t oldAllocatedMip = _allocatedMip;
allocateStorage(_allocatedMip + 1);
_populatedMip = std::max(_populatedMip, _allocatedMip);
uint16_t mips = _gpuObject.getNumMips();
// copy pre-existing mips
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
uint16_t targetMip = mip - _allocatedMip;
uint16_t sourceMip = targetMip + 1;
auto faces = getFaceCount(_target);
for (uint8_t face = 0; face < faces; ++face) {
glCopyImageSubData(
oldId, _target, sourceMip, 0, 0, face,
_id, _target, targetMip, 0, 0, face,
mipDimensions.x, mipDimensions.y, 1
);
(void)CHECK_GL_ERROR();
}
}
uint16_t numMips = _gpuObject.getNumMips();
copyTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip);
// destroy the old texture
glDeleteTextures(1, &oldId);
// update the memory usage

View file

@ -50,6 +50,10 @@ Context::Context(const Context& context) {
Context::~Context() {
}
const std::string& Context::getBackendVersion() const {
return _backend->getVersion();
}
void Context::beginFrame(const glm::mat4& renderPose) {
assert(!_frameActive);
_frameActive = true;

View file

@ -54,6 +54,9 @@ class Backend {
public:
virtual~ Backend() {};
virtual const std::string& getVersion() const = 0;
void setStereoState(const StereoState& stereo) { _stereo = stereo; }
virtual void render(const Batch& batch) = 0;
@ -153,6 +156,8 @@ public:
Context();
~Context();
const std::string& getBackendVersion() const;
void beginFrame(const glm::mat4& renderPose = glm::mat4());
void appendFrameBatch(Batch& batch);
FramePointer endFrame();

View file

@ -216,6 +216,7 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s
TexturePointer Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
TexturePointer tex = std::make_shared<Texture>(TextureUsageType::EXTERNAL);
tex->_type = TEX_2D;
tex->_texelFormat = Element::COLOR_RGBA_32;
tex->_maxMipLevel = 0;
tex->_sampler = sampler;
tex->setExternalRecycler(recycler);
@ -407,8 +408,12 @@ void Texture::setStoredMipFormat(const Element& format) {
_storage->setFormat(format);
}
const Element& Texture::getStoredMipFormat() const {
return _storage->getFormat();
Element Texture::getStoredMipFormat() const {
if (_storage) {
return _storage->getFormat();
} else {
return Element();
}
}
void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) {

View file

@ -285,7 +285,7 @@ public:
Stamp bumpStamp() { return ++_stamp; }
void setFormat(const Element& format) { _format = format; }
const Element& getFormat() const { return _format; }
Element getFormat() const { return _format; }
private:
Stamp _stamp { 0 };
@ -324,11 +324,11 @@ public:
void reset() override { }
protected:
std::shared_ptr<storage::FileStorage> maybeOpenFile();
std::shared_ptr<storage::FileStorage> maybeOpenFile() const;
std::mutex _cacheFileCreateMutex;
std::mutex _cacheFileWriteMutex;
std::weak_ptr<storage::FileStorage> _cacheFile;
mutable std::mutex _cacheFileCreateMutex;
mutable std::mutex _cacheFileWriteMutex;
mutable std::weak_ptr<storage::FileStorage> _cacheFile;
std::string _filename;
std::atomic<uint8_t> _minMipLevelAvailable;
@ -372,7 +372,7 @@ public:
bool isColorRenderTarget() const;
bool isDepthStencilRenderTarget() const;
const Element& getTexelFormat() const { return _texelFormat; }
Element getTexelFormat() const { return _texelFormat; }
Vec3u getDimensions() const { return Vec3u(_width, _height, _depth); }
uint16 getWidth() const { return _width; }
@ -468,7 +468,7 @@ public:
// Mip storage format is constant across all mips
void setStoredMipFormat(const Element& format);
const Element& getStoredMipFormat() const;
Element getStoredMipFormat() const;
// Manually allocate the mips down until the specified maxMip
// this is just allocating the sysmem version of it

View file

@ -128,7 +128,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
}
}
std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() {
std::shared_ptr<storage::FileStorage> KtxStorage::maybeOpenFile() const {
std::shared_ptr<storage::FileStorage> file = _cacheFile.lock();
if (file) {
return file;
@ -154,7 +154,8 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const {
auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face);
auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face);
if (faceSize != 0 && faceOffset != 0) {
result = std::make_shared<storage::FileStorage>(_filename.c_str())->createView(faceSize, faceOffset)->toMemoryStorage();
auto file = maybeOpenFile();
result = file->createView(faceSize, faceOffset)->toMemoryStorage();
}
return result;
}

View file

@ -334,6 +334,22 @@ private:
int _maxNumPixels;
};
NetworkTexture::~NetworkTexture() {
if (_ktxHeaderRequest || _ktxMipRequest) {
if (_ktxHeaderRequest) {
_ktxHeaderRequest->disconnect(this);
_ktxHeaderRequest->deleteLater();
_ktxHeaderRequest = nullptr;
}
if (_ktxMipRequest) {
_ktxMipRequest->disconnect(this);
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
}
TextureCache::requestCompleted(_self);
}
}
const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits<uint16_t>::max();
void NetworkTexture::makeRequest() {
if (!_sourceIsKTX) {
@ -398,13 +414,18 @@ void NetworkTexture::startRequestForNextMipLevel() {
}
if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) {
auto self = _self.lock();
if (!self) {
return;
}
_ktxResourceState = PENDING_MIP_REQUEST;
init();
float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip;
setLoadPriority(this, priority);
_url.setFragment(QString::number(_lowestKnownPopulatedMip - 1));
TextureCache::attemptRequest(_self);
TextureCache::attemptRequest(self);
}
}
@ -473,19 +494,16 @@ void NetworkTexture::ktxMipRequestFinished() {
texture->assignStoredMip(_ktxMipLevelRangeInFlight.first,
_ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data()));
_lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel();
}
else {
} else {
qWarning(networking) << "Trying to update mips but texture is null";
}
finishedLoading(true);
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
}
else {
} else {
finishedLoading(false);
if (handleFailedRequest(_ktxMipRequest->getResult())) {
_ktxResourceState = PENDING_MIP_REQUEST;
}
else {
} else {
qWarning(networking) << "Failed to load mip: " << _url;
_ktxResourceState = FAILED_TO_LOAD;
}
@ -497,8 +515,7 @@ void NetworkTexture::ktxMipRequestFinished() {
if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) {
startRequestForNextMipLevel();
}
}
else {
} else {
qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState;
}
}

View file

@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture {
public:
NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels);
~NetworkTexture() override;
QString getType() const override { return "NetworkTexture"; }

View file

@ -344,7 +344,7 @@ class Resource : public QObject {
public:
Resource(const QUrl& url);
~Resource();
virtual ~Resource();
virtual QString getType() const { return "Resource"; }

View file

@ -0,0 +1,82 @@
"use strict";
// request.js
//
// Created by Cisco Fresquet on 04/24/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global module */
// @module request
//
// This module contains the `request` module implementation
// ===========================================================================================
module.exports = {
// ------------------------------------------------------------------
request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (httpRequest.readyState >= READY_STATE_DONE) {
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
response = !error && httpRequest.responseText,
contentType = !error && httpRequest.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
if (error) {
response = { statusCode: httpRequest.status };
}
callback(error, response);
}
};
if (typeof options === 'string') {
options = { uri: options };
}
if (options.url) {
options.uri = options.url;
}
if (!options.method) {
options.method = 'GET';
}
if (options.body && (options.method === 'GET')) { // add query parameters
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
for (key in options.body) {
if (options.body.hasOwnProperty(key)) {
params.push(key + '=' + options.body[key]);
}
}
options.uri += appender + params.join('&');
delete options.body;
}
if (options.json) {
options.headers = options.headers || {};
options.headers["Content-type"] = "application/json";
options.body = JSON.stringify(options.body);
}
for (key in options.headers || {}) {
if (options.headers.hasOwnProperty(key)) {
httpRequest.setRequestHeader(key, options.headers[key]);
}
}
httpRequest.open(options.method, options.uri, true);
httpRequest.send(options.body);
}
};
// ===========================================================================================
// @function - debug logging
function debug() {
print('RequestModule | ' + [].slice.call(arguments).join(' '));
}

View file

@ -14,7 +14,7 @@
/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings,
Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset,
setGrabCommunications, Menu, HMD, isInEditMode */
setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
(function() { // BEGIN LOCAL_SCOPE
@ -75,7 +75,6 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe
//
// distant manipulation
//
var linearTimeScale = 0;
var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified
@ -155,7 +154,6 @@ var INCHES_TO_METERS = 1.0 / 39.3701;
// these control how long an abandoned pointer line or action will hang around
var ACTION_TTL = 15; // seconds
var ACTION_TTL_ZERO = 0; // seconds
var ACTION_TTL_REFRESH = 5;
var PICKS_PER_SECOND_PER_HAND = 60;
var MSECS_PER_SEC = 1000.0;
@ -193,7 +191,6 @@ var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"];
var holdEnabled = true;
var nearGrabEnabled = true;
var farGrabEnabled = true;
var farToNearGrab = false;
var myAvatarScalingEnabled = true;
var objectScalingEnabled = true;
var mostRecentSearchingHand = RIGHT_HAND;
@ -300,14 +297,13 @@ function getFingerWorldLocation(hand) {
// Object assign polyfill
if (typeof Object.assign != 'function') {
Object.assign = function(target, varArgs) {
'use strict';
if (target == null) {
if (target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
if (nextSource !== null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
@ -801,7 +797,7 @@ function calculateNearestStylusTarget(stylusTargets) {
}
return nearestStylusTarget;
};
}
// EntityPropertiesCache is a helper class that contains a cache of entity properties.
// the hope is to prevent excess calls to Entity.getEntityProperties()
@ -1229,8 +1225,9 @@ function MyController(hand) {
newState !== STATE_OVERLAY_LASER_TOUCHING)) {
return;
}
setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_DISTANCE_ROTATING)
|| (newState === STATE_NEAR_GRABBING));
setGrabCommunications((newState === STATE_DISTANCE_HOLDING) ||
(newState === STATE_DISTANCE_ROTATING) ||
(newState === STATE_NEAR_GRABBING));
if (WANT_DEBUG || WANT_DEBUG_STATE) {
var oldStateName = stateToName(this.state);
var newStateName = stateToName(newState);
@ -1428,7 +1425,7 @@ function MyController(hand) {
if (PICK_WITH_HAND_RAY) {
this.overlayLineOff();
}
}
};
this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) {
if (this.otherGrabbingLine === null) {
@ -1641,11 +1638,6 @@ function MyController(hand) {
var tipPosition = this.stylusTip.position;
var candidates = {
entities: [],
overlays: []
};
// build list of stylus targets, near the stylusTip
var stylusTargets = [];
var candidateEntities = Entities.findEntities(tipPosition, WEB_DISPLAY_STYLUS_DISTANCE);
@ -1972,9 +1964,10 @@ function MyController(hand) {
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
var otherHandControllerState = this.getOtherHandController().state;
var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING
|| otherHandControllerState === STATE_DISTANCE_HOLDING || otherHandControllerState === STATE_DISTANCE_ROTATING)
&& this.getOtherHandController().grabbedThingID === hotspot.entityID);
var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING ||
otherHandControllerState === STATE_DISTANCE_HOLDING ||
otherHandControllerState === STATE_DISTANCE_ROTATING) &&
this.getOtherHandController().grabbedThingID === hotspot.entityID);
var hasParent = true;
if (props.parentID === NULL_UUID) {
hasParent = false;
@ -1999,7 +1992,7 @@ function MyController(hand) {
return entityProps.cloneable;
}
return false;
}
};
this.entityIsGrabbable = function(entityID) {
var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID);
var props = entityPropertiesCache.getProps(entityID);
@ -2720,7 +2713,8 @@ function MyController(hand) {
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition));
var candidateHotSpotEntities = Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS);
var candidateHotSpotEntities =
Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS);
entityPropertiesCache.addEntities(candidateHotSpotEntities);
var potentialEquipHotspot = this.chooseBestEquipHotspotForFarToNearEquip(candidateHotSpotEntities);
@ -2730,40 +2724,23 @@ function MyController(hand) {
this.grabbedThingID = potentialEquipHotspot.entityID;
this.grabbedIsOverlay = false;
var success = Entities.updateAction(this.grabbedThingID, this.actionID, {
targetPosition: newTargetPosition,
linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
targetRotation: this.currentObjectRotation,
angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
ttl: ACTION_TTL_ZERO
});
if (success) {
this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC);
} else {
print("continueDistanceHolding -- updateAction failed");
}
Entities.deleteAction(this.grabbedThingID, this.actionID);
this.actionID = null;
this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedThingID).name + "'");
return;
}
}
var rayPositionOnEntity = Vec3.subtract(grabbedProperties.position, this.offsetPosition);
//Far to Near Grab: If object is draw by user inside FAR_TO_NEAR_GRAB_MAX_DISTANCE, grab it
if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, controllerLocation.position, FAR_TO_NEAR_GRAB_MAX_DISTANCE)) {
if (this.entityIsFarToNearGrabbable(rayPositionOnEntity,
controllerLocation.position,
FAR_TO_NEAR_GRAB_MAX_DISTANCE)) {
this.farToNearGrab = true;
var success = Entities.updateAction(this.grabbedThingID, this.actionID, {
targetPosition: newTargetPosition,
linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
targetRotation: this.currentObjectRotation,
angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
ttl: ACTION_TTL_ZERO // Overriding ACTION_TTL,Assign ACTION_TTL_ZERO so that the object is dropped down immediately after the trigger is released.
});
if (success) {
this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC);
} else {
print("continueDistanceHolding -- updateAction failed");
}
Entities.deleteAction(this.grabbedThingID, this.actionID);
this.actionID = null;
this.setState(STATE_NEAR_GRABBING , "near grab entity '" + this.grabbedThingID + "'");
return;
}
@ -2844,7 +2821,7 @@ function MyController(hand) {
COLORS_GRAB_DISTANCE_HOLD, this.grabbedThingID);
this.previousWorldControllerRotation = worldControllerRotation;
}
};
this.setupHoldAction = function() {
this.actionID = Entities.addAction("hold", this.grabbedThingID, {
@ -3043,15 +3020,14 @@ function MyController(hand) {
var worldEntities = Entities.findEntities(MyAvatar.position, 50);
var count = 0;
worldEntities.forEach(function(item) {
var item = Entities.getEntityProperties(item, ["name"]);
if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) {
var itemWE = Entities.getEntityProperties(item, ["name"]);
if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) {
count++;
}
})
});
var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0;
if (count >= limit && limit !== 0) {
delete limit;
return;
}
@ -3067,7 +3043,7 @@ function MyController(hand) {
delete cUserData.grabbableKey.cloneable;
delete cUserData.grabbableKey.cloneDynamic;
delete cUserData.grabbableKey.cloneLimit;
delete cProperties.id
delete cProperties.id;
cProperties.dynamic = dynamic;
cProperties.locked = false;
@ -3133,7 +3109,7 @@ function MyController(hand) {
_this.currentAngularVelocity = ZERO_VEC;
_this.prevDropDetected = false;
}
};
if (isClone) {
// 100 ms seems to be sufficient time to force the check even occur after the object has been initialized.
@ -3149,7 +3125,6 @@ function MyController(hand) {
var ttl = ACTION_TTL;
if (this.farToNearGrab) {
ttl = ACTION_TTL_ZERO; // farToNearGrab - Assign ACTION_TTL_ZERO so that, the object is dropped down immediately after the trigger is released.
if(!this.triggerClicked){
this.farToNearGrab = false;
}
@ -3322,7 +3297,7 @@ function MyController(hand) {
this.maybeScale(props);
}
if (this.actionID && this.actionTimeout - now < ttl * MSECS_PER_SEC) {
if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) {
// if less than a 5 seconds left, refresh the actions ttl
var success = Entities.updateAction(this.grabbedThingID, this.actionID, {
hand: this.hand === RIGHT_HAND ? "right" : "left",
@ -3761,10 +3736,12 @@ function MyController(hand) {
var TABLET_MAX_TOUCH_DISTANCE = 0.01;
if (this.stylusTarget) {
if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) {
if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) {
var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) {
distance2D(this.stylusTarget.position2D,
this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) {
sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget);
this.deadspotExpired = true;
}

View file

@ -104,97 +104,42 @@ input[type=button].naked:active {
*/
/*
// START styling of share bar
// START styling of share overlay
*/
.shareControls {
display: flex;
justify-content: space-between;
flex-direction: row;
align-items: center;
height: 45px;
line-height: 60px;
height: 65px;
line-height: 65px;
width: calc(100% - 8px);
position: absolute;
bottom: 4px;
left: 4px;
right: 4px;
}
.shareButtons {
display: flex;
align-items: center;
margin-left: 15px;
height: 100%;
width: 75%;
}
.blastToConnections {
text-align: left;
margin-right: 20px;
height: 29px;
}
.shareWithEveryone {
background: #DDDDDD url(../img/shareToFeed.png) no-repeat scroll center;
border-width: 0px;
text-align: left;
margin-right: 8px;
height: 29px;
width: 30px;
border-radius: 3px;
}
.facebookButton {
background-image: url(../img/fb_logo.png);
width: 29px;
height: 29px;
display: inline-block;
margin-right: 8px;
}
.twitterButton {
background-image: url(../img/twitter_logo.png);
width: 29px;
height: 29px;
display: inline-block;
margin-right: 8px;
border-radius: 3px;
}
.showShareButtonsButtonDiv {
display: inline-flex;
align-items: center;
font-family: Raleway-SemiBold;
font-size: 14px;
color: white;
width: 75px;
height: 100%;
margin-right: 10px;
margin-bottom: 0px;
}
.showShareButtonsButtonDiv.active:hover {
background-color: rgba(0, 0, 0, 0.45);
background-size: 2px;
}
.showShareButtonsButtonDiv > label {
text-shadow: 2px 2px 3px #000000;
margin-bottom: -14px;
margin-left: 12px;
}
.showShareButton {
width: 40px;
height: 40px;
border-radius: 50%;
border-width: 0;
margin-left: 5px;
outline: none;
}
.showShareButton.active {
border-color: #00b4ef;
border-width: 3px;
background-color: white;
}
.showShareButton.active:hover {
background-color: #afafaf;
}
.showShareButton.active:active {
background-color: white;
}
.showShareButton.inactive {
border-width: 0;
background-color: white;
}
.showShareButton.inactive:hover {
background-color: #afafaf;
}
.showShareButton.inactive:active {
background-color: white;
.showShareButtonsButtonDiv:hover > label {
text-shadow: none;
}
.showShareButtonDots {
display: block;
@ -203,15 +148,81 @@ input[type=button].naked:active {
font-family: HiFi-Glyphs;
font-size: 60px;
position: absolute;
right: 20px;
bottom: 12px;
color: #00b4ef;
left: 6px;
bottom: 32px;
color: white;
pointer-events: none;
}
.shareButtons {
display: flex;
align-items: flex-end;
height: 40px;
width: calc(100% - 60px);
margin-bottom: 25px;
margin-left: 0;
}
.shareButtons img {
width: 40px;
height: 40px;
}
.shareButton {
width: 40px;
height: 40px;
display: inline-block;
}
.shareButton.disabled {
background-color: #000000;
opacity: 0.5;
}
.shareControlsHelp {
height: 25px;
line-height: 25px;
position: absolute;
bottom: 0;
left: 73px;
right: 0;
font-family: Raleway-Regular;
font-weight: 500;
font-size: 16px;
padding-left: 8px;
color: white;
}
/*
// END styling of share overlay
*/
/*
// START styling of confirmation message
*/
.confirmationMessageContainer {
width: 100%;
height: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.45);
text-align: center;
left: 0;
top: 0;
pointer-events: none;
color: white;
font-weight: bold;
font-size: 16px;
}
.confirmationMessage {
width: 130px;
height: 130px;
margin: 50px auto 0 auto;
}
.confirmationMessage > img {
width: 72px;
height: 72px;
display: block;
margin: 0 auto;
padding: 10px 0 0 0;
}
/*
// END styling of uploading message
*/
/*
// START styling of snapshot controls (bottom panel) and its contents
*/

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M39.3,25.9c1.6,4.2,3.2,8.4,4.8,12.5c0.1,0.3,0.2,0.6,0,0.9c-0.3,0.3-0.6,0.3-1,0.2c-4-0.8-8.1-1.7-12.1-2.5
c-0.5-0.1-1-0.1-1.5,0.1c-0.5,0.2-1,0.4-1.5,0.6c-0.8,0.3-0.9,0.7-0.4,1.4c2.1,2.7,4.2,5.5,6.3,8.2c0.1,0.2,0.2,0.3,0.3,0.5
c0.2,0.4,0.1,0.7-0.3,1c-0.1,0.1-0.3,0.2-0.5,0.2c-1.5,0.6-3,1.1-4.4,1.7c-0.8,0.3-1,0.3-1.5-0.4c-2.3-3-4.6-5.9-6.9-8.9
c-0.5-0.6-0.6-0.6-1.3-0.4c-0.3,0.1-0.6,0.2-0.8,0.3c-1,0.3-2-0.1-2.3-1.1c-1.2-3.2-2.5-6.5-3.7-9.7c-0.4-1.1,0-2,1.1-2.5
c0.9-0.4,1.9-0.7,2.8-1.1c2.7-1,5.3-2,8-3c0.5-0.2,0.9-0.5,1.2-0.9c2.4-3.3,4.9-6.6,7.4-9.9c0.2-0.3,0.4-0.5,0.8-0.5
c0.4,0,0.5,0.4,0.7,0.7C36.1,17.5,37.7,21.7,39.3,25.9z"/>
<path class="st0" d="M38.2,15.6c-0.2,0-0.4-0.1-0.6-0.2c-0.6-0.3-0.7-1.1-0.4-1.6l3.7-6.1C41.3,7.1,42,7,42.6,7.3
c0.6,0.3,0.7,1.1,0.4,1.6l-3.7,6.1C39,15.4,38.6,15.6,38.2,15.6z"/>
<path class="st0" d="M53.1,36.7c-0.1,0-0.2,0-0.3,0l-7.4-1.6c-0.6-0.1-1-0.8-0.9-1.4c0.1-0.6,0.8-1,1.4-0.9l7.4,1.6
c0.6,0.1,1,0.8,0.9,1.4C54.1,36.4,53.6,36.7,53.1,36.7z"/>
<path class="st0" d="M42.5,24.8c-0.4,0-0.9-0.3-1.1-0.7c-0.3-0.6,0-1.3,0.6-1.6l7.5-3.5c0.6-0.3,1.3,0,1.6,0.6
c0.3,0.6,0,1.3-0.6,1.6L43,24.7C42.8,24.8,42.7,24.8,42.5,24.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M22.3,32c1.9,0,3.8,0,5.8,0c0.4,0,0.4,0,0.4,0.4c0,6,0,12.1,0,18.1h7.8c0-6,0-12.1,0-18.1c0-0.4,0-0.4,0.4-0.4
c1.9,0,3.8,0,5.8,0c0.3,0,0.3,0,0.4-0.3c0.2-1.2,0.3-2.5,0.5-3.7c0.1-1.2,0.3-2.3,0.4-3.5h-7.4c0-0.1,0-0.2,0-0.3c0-1.6,0-3.3,0-4.9
c0-0.4,0.1-0.8,0.2-1.2c0.3-1.1,1-1.7,2.1-1.9c0.5-0.1,1.1-0.1,1.6-0.1c1.1,0,2.3,0,3.4,0c0.3,0,0.3-0.1,0.3-0.3c0-2,0-4.1,0-6.1
c0-0.2-0.1-0.3-0.3-0.4c-2-0.2-4-0.3-6-0.2c-2.2,0.1-4.2,0.7-6,2.1c-1.5,1.3-2.4,2.9-2.8,4.8c-0.3,1-0.3,2.1-0.3,3.2
c0,1.7,0,3.4,0,5v0.4h-0.3c-2,0-3.9,0-5.9,0c-0.3,0-0.3,0.1-0.3,0.3c0,2.3,0,4.6,0,6.9C22,32,22,32,22.3,32z"/>
</svg>

After

Width:  |  Height:  |  Size: 1,012 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M12,52.3c6.5,0,13.2,0,20.2,0c6.5,0,13.4,0,20.6,0c3.5,0,5-1.6,5-5.6c0-10.2,0-20.5,0-30.7c0-4-1.3-5.5-5-5.5
c-4.8,0-9.6,0-14.4,0l-5.9,0c-2,0-4,0-6,0c-4.8,0-9.7,0-14.5,0c-3.5,0-4.9,1.5-4.9,5.5c0,10.3,0,20.6,0,30.9
C7.1,50.6,8.6,52.3,12,52.3z M41.9,43.3C41.9,43.3,41.9,43.3,41.9,43.3c0.1-0.1,0.1-0.1,0.1-0.2c1.4-1.5,2.8-3,4.3-4.6l0.8-0.9
c0.6,0.6,1.1,1.2,1.7,1.8c1.8,1.9,3.7,3.9,5.4,6c0.2,0.3,0.3,1.2,0,2.7c-0.1,0.1-0.5,0.2-1.5,0.4c-4.4,0.9-7.7-0.6-10.2-4.5
C42.4,43.8,42.2,43.5,41.9,43.3z M54.5,32.1c0,2.7,0,5.5,0,8.2L53.3,39c-1.4-1.5-2.8-3-4.2-4.4c-1.7-1.8-2.3-1.8-3.9,0
c-1.4,1.5-2.8,3-4.2,4.5l-1.5,1.6l-3.4-3.6c-3.4-3.7-6.8-7.3-10.2-10.9c-1.1-1.2-1.8-1.8-2.4-1.8c-0.6,0-1.3,0.6-2.4,1.8
c-2.6,2.8-5.4,5.5-8.1,8.4l-3,2.9v-4.4c0-1.6,0.2-3.2,0.2-4.8c0-4.1,0.1-8.4,0.1-12.5c0-0.7,0.1-1.1,0.3-1.3
c0.2-0.2,0.6-0.3,1.2-0.3c13.7,0,27.3,0,41.2,0c0.5,0,1,0,1.2,0.3c0.1,0.2,0.2,0.5,0.2,0.9C54.5,20.9,54.5,26.6,54.5,32.1z
M12.7,40.2c2.7-2.7,5.2-5.4,7.9-8.3c0.9-1,1.9-2.1,2.9-3.1L42,48.5c-0.2,0-0.4,0-0.6,0c-9.6,0-19.3,0-28.9,0c-0.8,0-1.5,0-1.7-0.2
c-0.2-0.2-0.2-1-0.3-2l0-0.1C10.2,43.7,10.9,42,12.7,40.2z"/>
<path class="st0" d="M37,23.7c0,2.1,1.4,3.8,3.3,3.8c1.9,0,3.4-1.6,3.4-3.7c0-2.1-1.4-3.8-3.3-3.8C38.5,19.9,37,21.6,37,23.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M13.1,43.9c4.5,0,8.2-0.9,11.8-3.6c-3.7-0.7-6.2-2.3-7.6-5.5c0.9-0.2,1.7-0.3,2.9-0.6c-3.6-1.7-5.8-4-6.2-7.9
c1.1,0.2,2,0.4,3.6,0.8c-3.3-3.5-4.5-6.9-2.5-11.1c4.8,4.9,10.2,8.2,16.6,8.5c0.7-2.3,1-4.6,2-6.4c2.2-3.9,7.4-5.2,11-2.6
c2,1.4,3.6,1.2,5.5,0.4c0.4-0.2,0.7-0.2,1.7-0.5c-1.1,1.5-1.8,2.5-2.7,3.8c1.1-0.1,2-0.3,2.9-0.4c0,0.2,0.1,0.3,0,0.4
c-2.6,3.4-2.9,4-3.8,8.9c-3.1,15.7-19.7,23.9-34,16.7C14.2,44.6,13.9,44.4,13.1,43.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1,3 +1,5 @@
/*jslint browser:true */
/*jslint maxlen: 180*/
"use strict";
//
// SnapshotReview.js
@ -13,27 +15,46 @@
var paths = [];
var idCounter = 0;
var imageCount = 0;
var blastShareText = "Blast to my Connections",
blastAlreadySharedText = "Already Blasted to Connections",
hifiShareText = "Share to Snaps Feed",
hifiAlreadySharedText = "Already Shared to Snaps Feed",
facebookShareText = "Share to Facebook",
twitterShareText = "Share to Twitter";
function showSetupInstructions() {
var snapshotImagesDiv = document.getElementById("snapshot-images");
snapshotImagesDiv.className = "snapshotInstructions";
snapshotImagesDiv.innerHTML = '<img class="centeredImage" src="./img/snapshotIcon.png" alt="Snapshot Instructions" width="64" height="64"/>' +
'<br/>' +
'<p>This app lets you take and share snaps and GIFs with your connections in High Fidelity.</p>' +
"<h4>Setup Instructions</h4>" +
"<p>Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:</p>" +
'<br/>' +
'<div style="text-align:center;">' +
'<input class="blueButton" style="margin-left:auto;margin-right:auto;width:130px;" type="button" value="CHOOSE" onclick="chooseSnapshotLocation()" />' +
'</div>';
'<br/>' +
'<p>Take and share snaps and GIFs with people in High Fidelity, Facebook, and Twitter.</p>' +
"<h4>Setup Instructions</h4>" +
"<p>Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:</p>" +
'<br/>' +
'<div style="text-align:center;">' +
'<input class="blueButton" style="margin-left:auto;margin-right:auto;width:130px;" type="button" value="CHOOSE" onclick="chooseSnapshotLocation()" />' +
'</div>';
document.getElementById("snap-button").disabled = true;
}
function showSetupComplete() {
var snapshotImagesDiv = document.getElementById("snapshot-images");
snapshotImagesDiv.className = "snapshotInstructions";
snapshotImagesDiv.innerHTML = '<img class="centeredImage" src="./img/snapshotIcon.png" alt="Snapshot Instructions" width="64" height="64"/>' +
'<br/>' +
"<h4>You're all set!</h4>" +
'<p>Try taking a snapshot by pressing the red button below.</p>';
'<br/>' +
'<div style="text-align:center;font-weight:bold;">' +
'<p>Snapshot location set.</p>' +
'<p>Press the big red button to take a snap!</p>' +
'</div>';
}
function showSnapshotInstructions() {
var snapshotImagesDiv = document.getElementById("snapshot-images");
snapshotImagesDiv.className = "snapshotInstructions";
snapshotImagesDiv.innerHTML = '<img class="centeredImage" src="./img/snapshotIcon.png" alt="Snapshot Instructions" width="64" height="64"/>' +
'<br/>' +
'<p>Take and share snaps and GIFs with people in High Fidelity, Facebook, and Twitter.</p>' +
'<br/>' +
'<div style="text-align:center;font-weight:bold;">' +
'<p>Press the big red button to take a snap!</p>' +
'</div>';
}
function chooseSnapshotLocation() {
EventBridge.emitWebEvent(JSON.stringify({
@ -52,13 +73,120 @@ function clearImages() {
imageCount = 0;
idCounter = 0;
}
function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) {
function selectImageToShare(selectedID, isSelected) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
}
var imageContainer = document.getElementById(selectedID),
image = document.getElementById(selectedID + 'img'),
shareBar = document.getElementById(selectedID + "shareBar"),
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"),
shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"),
itr,
containers = document.getElementsByClassName("shareControls");
if (isSelected) {
showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, false); };
showShareButtonsButtonDiv.classList.remove("inactive");
showShareButtonsButtonDiv.classList.add("active");
image.onclick = function () { selectImageToShare(selectedID, false); };
imageContainer.style.outline = "4px solid #00b4ef";
imageContainer.style.outlineOffset = "-4px";
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
shareButtonsDiv.style.visibility = "visible";
shareBarHelp.style.visibility = "visible";
for (itr = 0; itr < containers.length; itr += 1) {
var parentID = containers[itr].id.slice(0, 2);
if (parentID !== selectedID) {
selectImageToShare(parentID, false);
}
}
} else {
showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, true); };
showShareButtonsButtonDiv.classList.remove("active");
showShareButtonsButtonDiv.classList.add("inactive");
image.onclick = function () { selectImageToShare(selectedID, true); };
imageContainer.style.outline = "none";
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
shareButtonsDiv.style.visibility = "hidden";
shareBarHelp.style.visibility = "hidden";
}
}
function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
var shareBar = document.createElement("div"),
shareBarHelpID = parentID + "shareBarHelp",
shareButtonsDivID = parentID + "shareButtonsDiv",
showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv",
showShareButtonsLabelID = parentID + "showShareButtonsLabel",
blastToConnectionsButtonID = parentID + "blastToConnectionsButton",
shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton",
facebookButtonID = parentID + "facebookButton",
twitterButtonID = parentID + "twitterButton";
shareBar.id = parentID + "shareBar";
shareBar.className = "shareControls";
var shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
'<span class="showShareButtonDots">' +
'&#xe019;' +
'</div>' +
'</div>' +
'<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">';
if (canBlast) {
shareBarInnerHTML += '<div class="shareButton blastToConnections' + (blastButtonDisabled ? ' disabled' : '') + '" id="' + blastToConnectionsButtonID + '" onmouseover="shareButtonHovered(\'blast\', ' + parentID + ')" onclick="' + (blastButtonDisabled ? '' : 'blastToConnections(' + parentID + ', ' + isGif + ')') + '"><img src="img/blast_icon.svg"></div>';
}
shareBarInnerHTML += '<div class="shareButton shareWithEveryone' + (hifiButtonDisabled ? ' disabled' : '') + '" id="' + shareWithEveryoneButtonID + '" onmouseover="shareButtonHovered(\'hifi\', ' + parentID + ')" onclick="' + (hifiButtonDisabled ? '' : 'shareWithEveryone(' + parentID + ', ' + isGif + ')') + '"><img src="img/hifi_icon.svg" style="width:35px;height:35px;margin:2px 0 0 2px;"></div>' +
'<a class="shareButton facebookButton" id="' + facebookButtonID + '" onmouseover="shareButtonHovered(\'facebook\', ' + parentID + ')" onclick="shareButtonClicked(\'facebook\', ' + parentID + ')"><img src="img/fb_icon.svg"></a>' +
'<a class="shareButton twitterButton" id="' + twitterButtonID + '" onmouseover="shareButtonHovered(\'twitter\', ' + parentID + ')" onclick="shareButtonClicked(\'twitter\', ' + parentID + ')"><img src="img/twitter_icon.svg"></a>' +
'</div>';
shareBar.innerHTML = shareBarInnerHTML;
shareBar.innerHTML += '<div class="shareControlsHelp" id="' + shareBarHelpID + '" style="visibility:hidden;' + ((canBlast && blastButtonDisabled || !canBlast && hifiButtonDisabled) ? "background-color:#000;opacity:0.5;" : "") + '"></div>';
// Add onclick handler to parent DIV's img to toggle share buttons
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); };
return shareBar;
}
function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) {
if (divID.id) {
divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
}
document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast));
if (divID === "p0") {
selectImageToShare(divID, true);
if (canBlast) {
shareButtonHovered('blast', divID);
} else {
shareButtonHovered('hifi', divID);
}
}
}
function shareForUrl(selectedID) {
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "shareSnapshotForUrl",
data: paths[parseInt(selectedID.substring(1), 10)]
}));
}
function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) {
if (!image_data.localPath) {
return;
}
var id = "p" + idCounter++;
// imageContainer setup
var imageContainer = document.createElement("DIV");
var id = "p" + (idCounter++),
imageContainer = document.createElement("DIV"),
img = document.createElement("IMG"),
isGif;
imageContainer.id = id;
imageContainer.style.width = "95%";
imageContainer.style.height = "240px";
@ -67,160 +195,264 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b
imageContainer.style.justifyContent = "center";
imageContainer.style.alignItems = "center";
imageContainer.style.position = "relative";
// img setup
var img = document.createElement("IMG");
img.id = id + "img";
if (imageCount > 1) {
img.setAttribute("class", "multiple");
}
img.src = image_data.localPath;
isGif = img.src.split('.').pop().toLowerCase() === "gif";
imageContainer.appendChild(img);
document.getElementById("snapshot-images").appendChild(imageContainer);
paths.push(image_data.localPath);
var isGif = img.src.split('.').pop().toLowerCase() === "gif";
if (isGif) {
imageContainer.innerHTML += '<span class="gifLabel">GIF</span>';
}
if (!isGifLoading && !isShowingPreviousImages && canShare) {
appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true);
shareForUrl(id);
} else if (isShowingPreviousImages && canShare && image_data.story_id) {
appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled)
}
if (isShowingPreviousImages && image_data.story_id) {
appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast);
updateShareInfo(id, image_data.story_id);
}
}
function appendShareBar(divID, story_id, isGif, blastButtonDisabled, hifiButtonDisabled) {
var story_url = "https://highfidelity.com/user_stories/" + story_id;
var parentDiv = document.getElementById(divID);
parentDiv.setAttribute('data-story-id', story_id);
document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, blastButtonDisabled, hifiButtonDisabled));
if (divID === "p0") {
selectImageToShare(divID, true);
function showConfirmationMessage(selectedID, destination) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
}
var opacity = 2.0,
fadeRate = 0.05,
timeBetweenFadesMS = 50,
confirmationMessageContainer = document.createElement("div"),
confirmationMessage = document.createElement("div");
confirmationMessageContainer.className = "confirmationMessageContainer";
confirmationMessage.className = "confirmationMessage";
var socialIcon = document.createElement("img");
switch (destination) {
case 'blast':
socialIcon.src = "img/blast_icon.svg";
confirmationMessage.appendChild(socialIcon);
confirmationMessage.innerHTML += '<span>Blast Sent!</span>';
confirmationMessage.style.backgroundColor = "#EA4C5F";
break;
case 'hifi':
socialIcon.src = "img/hifi_icon.svg";
confirmationMessage.appendChild(socialIcon);
confirmationMessage.innerHTML += '<span>Snap Shared!</span>';
confirmationMessage.style.backgroundColor = "#1FC6A6";
break;
}
confirmationMessageContainer.appendChild(confirmationMessage);
document.getElementById(selectedID).appendChild(confirmationMessageContainer);
setInterval(function () {
if (opacity <= fadeRate) {
confirmationMessageContainer.remove();
}
opacity -= fadeRate;
confirmationMessageContainer.style.opacity = opacity;
}, timeBetweenFadesMS);
}
function showUploadingMessage(selectedID, destination) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
}
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp");
shareBarHelp.innerHTML = '<img style="display:inline;width:25px;height:25px;" src="./img/loader.gif"></img><span style="position:relative;margin-left:5px;bottom:7px;">Preparing to Share</span>';
shareBarHelp.setAttribute("data-destination", destination);
}
function hideUploadingMessageAndShare(selectedID, storyID) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
}
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
shareBarHelpDestination = shareBarHelp.getAttribute("data-destination");
if (shareBarHelpDestination) {
switch (shareBarHelpDestination) {
case 'blast':
blastToConnections(selectedID, selectedID === "p1");
shareBarHelp.innerHTML = blastAlreadySharedText;
break;
case 'hifi':
shareWithEveryone(selectedID, selectedID === "p1");
shareBarHelp.innerHTML = hifiAlreadySharedText;
break;
case 'facebook':
var facebookButton = document.getElementById(selectedID + "facebookButton");
window.open(facebookButton.getAttribute("href"), "_blank");
shareBarHelp.innerHTML = facebookShareText;
break;
case 'twitter':
var twitterButton = document.getElementById(selectedID + "twitterButton");
window.open(twitterButton.getAttribute("href"), "_blank");
shareBarHelp.innerHTML = twitterShareText;
break;
}
shareBarHelp.setAttribute("data-destination", "");
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "removeFromStoryIDsToMaybeDelete",
story_id: storyID
}));
}
}
function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButtonDisabled) {
var shareBar = document.createElement("div");
shareBar.id = parentID + "shareBar";
shareBar.className = "shareControls";
var shareButtonsDivID = parentID + "shareButtonsDiv";
var showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv";
var showShareButtonsButtonID = parentID + "showShareButtonsButton";
var showShareButtonsLabelID = parentID + "showShareButtonsLabel";
var blastToConnectionsButtonID = parentID + "blastToConnectionsButton";
var shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton";
var facebookButtonID = parentID + "facebookButton";
var twitterButtonID = parentID + "twitterButton";
shareBar.innerHTML += '' +
'<div class="shareButtons" id="' + shareButtonsDivID + '" style="visibility:hidden">' +
'<input type="button"' + (blastButtonDisabled ? ' disabled' : '') + ' class="blastToConnections blueButton" id="' + blastToConnectionsButtonID + '" value="BLAST TO MY CONNECTIONS" onclick="blastToConnections(' + parentID + ', ' + isGif + ')" />' +
'<input type="button"' + (hifiButtonDisabled ? ' disabled' : '') + ' class="shareWithEveryone" id="' + shareWithEveryoneButtonID + '" onclick="shareWithEveryone(' + parentID + ', ' + isGif + ')" />' +
'<a class="facebookButton" id="' + facebookButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://www.facebook.com/dialog/feed?app_id=1585088821786423&link=' + shareURL + '"></a>' +
'<a class="twitterButton" id="' + twitterButtonID + '" onclick="shareButtonClicked(' + parentID + ')" target="_blank" href="https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi"></a>' +
'</div>' +
'<div class="showShareButtonsButtonDiv" id="' + showShareButtonsButtonDivID + '">' +
'<label id="' + showShareButtonsLabelID + '" for="' + showShareButtonsButtonID + '">SHARE</label>' +
'<input type="button" class="showShareButton inactive" id="' + showShareButtonsButtonID + '" onclick="selectImageToShare(' + parentID + ', true)" />' +
'<div class="showShareButtonDots">' +
'&#xe019;' +
'</div>' +
'</div>';
function updateShareInfo(containerID, storyID) {
if (containerID.id) {
containerID = containerID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID
}
var shareBar = document.getElementById(containerID + "shareBar"),
parentDiv = document.getElementById(containerID),
shareURL = "https://highfidelity.com/user_stories/" + storyID,
facebookButton = document.getElementById(containerID + "facebookButton"),
twitterButton = document.getElementById(containerID + "twitterButton");
// Add onclick handler to parent DIV's img to toggle share buttons
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true) };
parentDiv.setAttribute('data-story-id', storyID);
return shareBar;
facebookButton.setAttribute("target", "_blank");
facebookButton.setAttribute("href", 'https://www.facebook.com/dialog/feed?app_id=1585088821786423&link=' + shareURL);
twitterButton.setAttribute("target", "_blank");
twitterButton.setAttribute("href", 'https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi');
hideUploadingMessageAndShare(containerID, storyID);
}
function selectImageToShare(selectedID, isSelected) {
function blastToConnections(selectedID, isGif) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
}
var imageContainer = document.getElementById(selectedID);
var image = document.getElementById(selectedID + 'img');
var shareBar = document.getElementById(selectedID + "shareBar");
var shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv");
var showShareButtonsButton = document.getElementById(selectedID + "showShareButtonsButton");
if (isSelected) {
showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, false) };
showShareButtonsButton.classList.remove("inactive");
showShareButtonsButton.classList.add("active");
var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"),
shareBar = document.getElementById(selectedID + "shareBar"),
shareBarHelp = document.getElementById(selectedID + "shareBarHelp");
blastToConnectionsButton.onclick = function () { };
image.onclick = function () { selectImageToShare(selectedID, false) };
imageContainer.style.outline = "4px solid #00b4ef";
imageContainer.style.outlineOffset = "-4px";
var storyID = document.getElementById(selectedID).getAttribute("data-story-id");
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
shareButtonsDiv.style.visibility = "visible";
var containers = document.getElementsByClassName("shareControls");
var parentID;
for (var i = 0; i < containers.length; ++i) {
parentID = containers[i].id.slice(0, 2);
if (parentID !== selectedID) {
selectImageToShare(parentID, false);
}
}
if (storyID) {
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "blastToConnections",
story_id: storyID,
isGif: isGif
}));
showConfirmationMessage(selectedID, 'blast');
blastToConnectionsButton.classList.add("disabled");
blastToConnectionsButton.style.backgroundColor = "#000000";
blastToConnectionsButton.style.opacity = "0.5";
shareBarHelp.style.backgroundColor = "#000000";
shareBarHelp.style.opacity = "0.5";
} else {
showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true) };
showShareButtonsButton.classList.remove("active");
showShareButtonsButton.classList.add("inactive");
image.onclick = function () { selectImageToShare(selectedID, true) };
imageContainer.style.outline = "none";
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
shareButtonsDiv.style.visibility = "hidden";
showUploadingMessage(selectedID, 'blast');
}
}
function shareForUrl(selectedID) {
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "shareSnapshotForUrl",
data: paths[parseInt(selectedID.substring(1))]
}));
}
function blastToConnections(selectedID, isGif) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
document.getElementById(selectedID + "blastToConnectionsButton").disabled = true;
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "blastToConnections",
story_id: document.getElementById(selectedID).getAttribute("data-story-id"),
isGif: isGif
}));
}
function shareWithEveryone(selectedID, isGif) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
}
document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true;
var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"),
shareBar = document.getElementById(selectedID + "shareBar"),
shareBarHelp = document.getElementById(selectedID + "shareBarHelp");
shareWithEveryoneButton.onclick = function () { };
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "shareSnapshotWithEveryone",
story_id: document.getElementById(selectedID).getAttribute("data-story-id"),
isGif: isGif
}));
var storyID = document.getElementById(selectedID).getAttribute("data-story-id");
if (storyID) {
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "shareSnapshotWithEveryone",
story_id: storyID,
isGif: isGif
}));
showConfirmationMessage(selectedID, 'hifi');
shareWithEveryoneButton.classList.add("disabled");
shareWithEveryoneButton.style.backgroundColor = "#000000";
shareWithEveryoneButton.style.opacity = "0.5";
shareBarHelp.style.backgroundColor = "#000000";
shareBarHelp.style.opacity = "0.5";
} else {
showUploadingMessage(selectedID, 'hifi');
}
}
function shareButtonClicked(selectedID) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "shareButtonClicked",
story_id: document.getElementById(selectedID).getAttribute("data-story-id")
}));
}
function cancelSharing(selectedID) {
selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID
var shareBar = document.getElementById(selectedID + "shareBar");
function shareButtonHovered(destination, selectedID) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
}
var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes,
itr;
shareBar.style.display = "inline";
for (itr = 0; itr < shareButtonsDiv.length; itr += 1) {
shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)";
}
shareBarHelp.style.opacity = "1.0";
switch (destination) {
case 'blast':
var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton");
if (!blastToConnectionsButton.classList.contains("disabled")) {
shareBarHelp.style.backgroundColor = "#EA4C5F";
shareBarHelp.style.opacity = "1.0";
blastToConnectionsButton.style.backgroundColor = "#EA4C5F";
blastToConnectionsButton.style.opacity = "1.0";
shareBarHelp.innerHTML = blastShareText;
} else {
shareBarHelp.style.backgroundColor = "#000000";
shareBarHelp.style.opacity = "0.5";
blastToConnectionsButton.style.backgroundColor = "#000000";
blastToConnectionsButton.style.opacity = "0.5";
shareBarHelp.innerHTML = blastAlreadySharedText;
}
break;
case 'hifi':
var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton");
if (!shareWithEveryoneButton.classList.contains("disabled")) {
shareBarHelp.style.backgroundColor = "#1FC6A6";
shareBarHelp.style.opacity = "1.0";
shareWithEveryoneButton.style.backgroundColor = "#1FC6A6";
shareWithEveryoneButton.style.opacity = "1.0";
shareBarHelp.innerHTML = hifiShareText;
} else {
shareBarHelp.style.backgroundColor = "#000000";
shareBarHelp.style.opacity = "0.5";
shareWithEveryoneButton.style.backgroundColor = "#000000";
shareWithEveryoneButton.style.opacity = "0.5";
shareBarHelp.innerHTML = hifiAlreadySharedText;
}
break;
case 'facebook':
shareBarHelp.style.backgroundColor = "#3C58A0";
shareBarHelp.innerHTML = facebookShareText;
document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0";
break;
case 'twitter':
shareBarHelp.style.backgroundColor = "#00B4EE";
shareBarHelp.innerHTML = twitterShareText;
document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE";
break;
}
}
function shareButtonClicked(destination, selectedID) {
if (selectedID.id) {
selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID
}
var storyID = document.getElementById(selectedID).getAttribute("data-story-id");
if (!storyID) {
showUploadingMessage(selectedID, destination);
}
}
function handleCaptureSetting(setting) {
var stillAndGif = document.getElementById('stillAndGif');
var stillOnly = document.getElementById('stillOnly');
var stillAndGif = document.getElementById('stillAndGif'),
stillOnly = document.getElementById('stillOnly');
stillAndGif.checked = setting;
stillOnly.checked = !setting;
@ -229,19 +461,20 @@ function handleCaptureSetting(setting) {
type: "snapshot",
action: "captureStillAndGif"
}));
}
};
stillOnly.onclick = function () {
EventBridge.emitWebEvent(JSON.stringify({
type: "snapshot",
action: "captureStillOnly"
}));
}
};
}
window.onload = function () {
// Uncomment the line below to test functionality in a browser.
// See definition of "testInBrowser()" to modify tests.
//testInBrowser(false);
//testInBrowser(3);
openEventBridge(function () {
// Set up a handler for receiving the data, and tell the .js we are ready to receive it.
EventBridge.scriptEventReceived.connect(function (message) {
@ -251,7 +484,9 @@ window.onload = function () {
if (message.type !== "snapshot") {
return;
}
var messageOptions = message.options;
switch (message.action) {
case 'showSetupInstructions':
showSetupInstructions();
@ -265,38 +500,41 @@ window.onload = function () {
break;
case 'showPreviousImages':
clearImages();
var messageOptions = message.options;
imageCount = message.image_data.length;
message.image_data.forEach(function (element, idx, array) {
addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled);
});
if (imageCount > 0) {
message.image_data.forEach(function (element, idx) {
addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast);
});
} else {
showSnapshotInstructions();
}
break;
case 'addImages':
// The last element of the message contents list contains a bunch of options,
// including whether or not we can share stuff
// The other elements of the list contain image paths.
var messageOptions = message.options;
if (messageOptions.containsGif) {
if (messageOptions.processingGif) {
imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon
message.image_data.push({ localPath: messageOptions.loadingGifPath });
message.image_data.forEach(function (element, idx, array) {
message.image_data.forEach(function (element, idx) {
addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false);
});
} else {
var gifPath = message.image_data[0].localPath;
var p1img = document.getElementById('p1img');
var gifPath = message.image_data[0].localPath,
p1img = document.getElementById('p1img');
p1img.src = gifPath;
paths[1] = gifPath;
if (messageOptions.canShare) {
shareForUrl("p1");
appendShareBar("p1", true, false, false, true);
}
}
} else {
imageCount = message.image_data.length;
message.image_data.forEach(function (element, idx, array) {
message.image_data.forEach(function (element) {
addImage(element, false, messageOptions.canShare, false);
});
}
@ -306,7 +544,7 @@ window.onload = function () {
break;
case 'snapshotUploadComplete':
var isGif = message.image_url.split('.').pop().toLowerCase() === "gif";
appendShareBar(isGif ? "p1" : "p0", message.story_id, isGif);
updateShareInfo(isGif ? "p1" : "p0", message.story_id);
break;
default:
console.log("Unknown message action received in SnapshotReview.js.");
@ -333,13 +571,24 @@ function takeSnapshot() {
}));
}
function testInBrowser(isTestingSetupInstructions) {
if (isTestingSetupInstructions) {
function testInBrowser(test) {
if (test === 0) {
showSetupInstructions();
} else {
imageCount = 1;
} else if (test === 1) {
imageCount = 2;
//addImage({ localPath: 'http://lorempixel.com/553/255' });
addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.gif' }, false, true, true, false, false);
addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.jpg' }, false, true, true, false, false);
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
} else if (test === 2) {
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
showConfirmationMessage("p0", 'blast');
showConfirmationMessage("p1", 'hifi');
} else if (test === 3) {
imageCount = 2;
//addImage({ localPath: 'http://lorempixel.com/553/255' });
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true);
addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true);
showUploadingMessage("p0", 'hifi');
}
}

View file

@ -11,8 +11,11 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () { // BEGIN LOCAL_SCOPE
var request = Script.require('request').request;
var LABEL = "makeUserConnection";
var MAX_AVATAR_DISTANCE = 0.2; // m
var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed
@ -126,61 +129,6 @@
function cleanId(guidWithCurlyBraces) {
return guidWithCurlyBraces.slice(1, -1);
}
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (httpRequest.readyState >= READY_STATE_DONE) {
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
response = !error && httpRequest.responseText,
contentType = !error && httpRequest.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
if (error) {
response = {statusCode: httpRequest.status};
}
callback(error, response);
}
};
if (typeof options === 'string') {
options = {uri: options};
}
if (options.url) {
options.uri = options.url;
}
if (!options.method) {
options.method = 'GET';
}
if (options.body && (options.method === 'GET')) { // add query parameters
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
for (key in options.body) {
if (options.body.hasOwnProperty(key)) {
params.push(key + '=' + options.body[key]);
}
}
options.uri += appender + params.join('&');
delete options.body;
}
if (options.json) {
options.headers = options.headers || {};
options.headers["Content-type"] = "application/json";
options.body = JSON.stringify(options.body);
}
for (key in options.headers || {}) {
if (options.headers.hasOwnProperty(key)) {
httpRequest.setRequestHeader(key, options.headers[key]);
}
}
httpRequest.open(options.method, options.uri, true);
httpRequest.send(options.body);
}
function handToString(hand) {
if (hand === Controller.Standard.RightHand) {
@ -514,7 +462,7 @@
endHandshakeAnimation();
// No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us
// in a weird state.
request({uri: requestUrl, method: 'DELETE'}, debug);
request({ uri: requestUrl, method: 'DELETE' }, debug);
}
function updateTriggers(value, fromKeyboard, hand) {

View file

@ -14,6 +14,8 @@
(function() { // BEGIN LOCAL_SCOPE
var request = Script.require('request').request;
var populateNearbyUserList, color, textures, removeOverlays,
controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged,
receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL,
@ -331,55 +333,6 @@ function updateUser(data) {
//
// These are prototype versions that will be changed when the back end changes.
var METAVERSE_BASE = location.metaverseServerUrl;
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (httpRequest.readyState >= READY_STATE_DONE) {
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
response = !error && httpRequest.responseText,
contentType = !error && httpRequest.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
callback(error, response);
}
};
if (typeof options === 'string') {
options = {uri: options};
}
if (options.url) {
options.uri = options.url;
}
if (!options.method) {
options.method = 'GET';
}
if (options.body && (options.method === 'GET')) { // add query parameters
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
for (key in options.body) {
params.push(key + '=' + options.body[key]);
}
options.uri += appender + params.join('&');
delete options.body;
}
if (options.json) {
options.headers = options.headers || {};
options.headers["Content-type"] = "application/json";
options.body = JSON.stringify(options.body);
}
for (key in options.headers || {}) {
httpRequest.setRequestHeader(key, options.headers[key]);
}
httpRequest.open(options.method, options.uri, true);
httpRequest.send(options.body);
}
function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise.
request({

View file

@ -182,7 +182,6 @@ function onMessage(message) {
break;
case 'blastToConnections':
isLoggedIn = Account.isLoggedIn();
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
if (message.isGif) {
Settings.setValue("previousAnimatedSnapBlastingDisabled", true);
} else {
@ -242,7 +241,6 @@ function onMessage(message) {
break;
case 'shareSnapshotWithEveryone':
isLoggedIn = Account.isLoggedIn();
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
if (message.isGif) {
Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true);
} else {
@ -283,8 +281,7 @@ function onMessage(message) {
snapshotToShareAfterLogin = { path: message.data, href: message.href || href };
}
break;
case 'shareButtonClicked':
print('Twitter or FB "Share" button clicked! Removing ID', message.story_id, 'from storyIDsToMaybeDelete[].');
case 'removeFromStoryIDsToMaybeDelete':
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1);
print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
break;
@ -314,7 +311,8 @@ function onButtonClicked() {
snapshotOptions = {
containsGif: previousAnimatedSnapPath !== "",
processingGif: false,
shouldUpload: false
shouldUpload: false,
canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID")
}
imageData = [];
if (previousStillSnapPath !== "") {
@ -395,7 +393,9 @@ function takeSnapshot() {
resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked.
reticleVisible = Reticle.visible;
Reticle.visible = false;
Reticle.allowMouseCapture = false;
if (!HMD.active) {
Reticle.allowMouseCapture = false;
}
var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true);
if (includeAnimated) {

View file

@ -14,6 +14,9 @@
//
(function () { // BEGIN LOCAL_SCOPE
var request = Script.require('request').request;
var gotoQmlSource = "TabletAddressDialog.qml";
var buttonName = "GOTO";
var onGotoScreen = false;
@ -30,54 +33,7 @@
text: buttonName,
sortOrder: 8
});
function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request.
var httpRequest = new XMLHttpRequest(), key;
// QT bug: apparently doesn't handle onload. Workaround using readyState.
httpRequest.onreadystatechange = function () {
var READY_STATE_DONE = 4;
var HTTP_OK = 200;
if (httpRequest.readyState >= READY_STATE_DONE) {
var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText,
response = !error && httpRequest.responseText,
contentType = !error && httpRequest.getResponseHeader('content-type');
if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc.
try {
response = JSON.parse(response);
} catch (e) {
error = e;
}
}
callback(error, response);
}
};
if (typeof options === 'string') {
options = {uri: options};
}
if (options.url) {
options.uri = options.url;
}
if (!options.method) {
options.method = 'GET';
}
if (options.body && (options.method === 'GET')) { // add query parameters
var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&';
for (key in options.body) {
params.push(key + '=' + options.body[key]);
}
options.uri += appender + params.join('&');
delete options.body;
}
if (options.json) {
options.headers = options.headers || {};
options.headers["Content-type"] = "application/json";
options.body = JSON.stringify(options.body);
}
for (key in options.headers || {}) {
httpRequest.setRequestHeader(key, options.headers[key]);
}
httpRequest.open(options.method, options.uri, true);
httpRequest.send(options.body);
}
function fromQml(message) {
var response = {id: message.id, jsonrpc: "2.0"};
switch (message.method) {
@ -99,6 +55,7 @@
// No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway.
});
}
var hasEventBridge = false;
function wireEventBridge(on) {
if (on) {