1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-05 10:39:25 +02:00
OpenSTF/res/app/components/stf/screen/screen-directive.js
Gunther Brunner 38d20eba9a Revert "Revert "Fix canvas rotation being off on devices which are naturally landscape.""
Reverting the Revert because we found the reason for the blurry image.

This reverts commit 7afd816cf918ed94b2ebf7671f804ae0f769fcb9.
2014-09-12 15:59:13 +09:00

390 lines
11 KiB
JavaScript

var FastImageRender = require('./fast-image-render').FastImageRender
var _ = require('lodash')
module.exports = function DeviceScreenDirective($document, ScalingService,
VendorUtil, PageVisibilityService, BrowserInfo, $timeout) {
return {
restrict: 'E',
template: require('./screen.jade'),
link: function (scope, element) {
var canvas = element.find('canvas')[0]
var imageRender = new FastImageRender(canvas, {
render: 'canvas',
timeout: 3000
})
var guestDisplayDensity = setDisplayDensity(1.5)
//var guestDisplayRotation = 0
var finger = element.find('span')
var input = element.find('input')
var boundingWidth = 0 // TODO: cache inside FastImageRender?
var boundingHeight = 0
var cachedBoundingWidth = 0
var cachedBoundingHeight = 0
var cachedImageWidth = 0
var cachedImageHeight = 0
var cachedRotation = 0
var rotation = 0
var loading = false
var scaler
var seq = 0
var cssTransform = VendorUtil.style(['transform', 'webkitTransform'])
// NOTE: instead of fa-pane-resize, a fa-child-pane-resize could be better
var onPanelResizeThrottled = _.throttle(updateBounds, 16)
scope.$on('fa-pane-resize', onPanelResizeThrottled)
function setDisplayDensity(forRetina) {
// FORCE
forRetina = 1.2
guestDisplayDensity =
BrowserInfo.mobile && BrowserInfo.retina ? forRetina : 1
return guestDisplayDensity
}
function sendTouch(type, e) {
var x = e.offsetX || e.layerX || 0
var y = e.offsetY || e.layerY || 0
var r = scope.device.display.rotation
if (BrowserInfo.touch) {
if (e.touches && e.touches.length) {
x = e.touches[0].pageX
y = e.touches[0].pageY
} else if (e.changedTouches && e.changedTouches.length) {
x = e.changedTouches[0].pageX
y = e.changedTouches[0].pageY
}
}
var scaled = scaler.coords(boundingWidth, boundingHeight, x, y, r)
finger[0].style[cssTransform] =
'translate3d(' + x + 'px,' + y + 'px,0)'
scope.control[type](
seq++, scaled.xP, scaled.yP
)
}
function stopTouch() {
element.removeClass('fingering')
if (BrowserInfo.touch) {
element.unbind('touchmove', moveListener)
$document.unbind('touchend', upListener)
$document.unbind('touchleave', upListener)
} else {
element.unbind('mousemove', moveListener)
$document.unbind('mouseup', upListener)
$document.unbind('mouseleave', upListener)
}
seq = 0
}
function updateBounds() {
boundingWidth = element[0].offsetWidth
boundingHeight = element[0].offsetHeight
// TODO: element is an object HTMLUnknownElement in IE9
// Developer error, let's try to reduce debug time
if (!boundingWidth || !boundingHeight) {
throw new Error(
'Unable to update display size; container must have dimensions'
)
}
}
function downListener(e) {
e.preventDefault()
if (!BrowserInfo.touch) {
input[0].focus()
element.addClass('fingering')
}
sendTouch('touchDown', e)
if (BrowserInfo.touch) {
element.bind('touchmove', moveListener)
$document.bind('touchend', upListener)
$document.bind('touchleave', upListener)
} else {
element.bind('mousemove', moveListener)
$document.bind('mouseup', upListener)
$document.bind('mouseleave', upListener)
}
}
function moveListener(e) {
sendTouch('touchMove', e)
}
function upListener(e) {
sendTouch('touchUp', e)
stopTouch()
}
function isChangeCharsetKey(e) {
// Add any special key here for changing charset
//console.log('e', e)
// Chrome/Safari/Opera
if (
// Mac | Kinesis keyboard | Karabiner | Latin key, Kana key
e.keyCode === 0 && e.keyIdentifier === 'U+0010' ||
// Mac | MacBook Pro keyboard | Latin key, Kana key
e.keyCode === 0 && e.keyIdentifier === 'U+0020' ||
// Win | Lenovo X230 keyboard | Alt+Latin key
e.keyCode === 246 && e.keyIdentifier === 'U+00F6' ||
// Win | Lenovo X230 keyboard | Convert key
e.keyCode === 28 && e.keyIdentifier === 'U+001C'
) {
return true
}
// Firefox
switch (e.key) {
case 'Convert': // Windows | Convert key
case 'Alphanumeric': // Mac | Latin key
case 'RomanCharacters': // Windows/Mac | Latin key
case 'KanjiMode': // Windows/Mac | Kana key
return true
}
return false
}
function keyupSpecialKeys(e) {
var specialKey = false
if (isChangeCharsetKey(e)) {
specialKey = true
scope.control.keyPress('switch_charset')
}
if (specialKey) {
e.preventDefault()
}
return specialKey
}
function keydownListener(e) {
scope.control.keyDown(e.keyCode)
}
function keyupListener(e) {
if (!keyupSpecialKeys(e)) {
scope.control.keyUp(e.keyCode)
}
}
function keypressListener(e) {
e.preventDefault() // no need to change value
scope.control.type(String.fromCharCode(e.charCode))
}
function pasteListener(e) {
e.preventDefault() // no need to change value
scope.control.paste(e.clipboardData.getData('text/plain'))
}
function copyListener(e) {
scope.control.getClipboardContent()
// @TODO: OK, this basically copies last clipboard content
if (scope.control.clipboardContent) {
e.clipboardData.setData("text/plain", scope.control.clipboardContent)
}
e.preventDefault()
}
scope.retryLoadingScreen = function () {
if (scope.displayError === 'secure') {
scope.control.home()
}
$timeout(maybeLoadScreen, 3000)
}
function maybeLoadScreen() {
if (!loading && scope.$parent.showScreen && scope.device) {
var w, h
switch (rotation) {
case 0:
case 180:
w = boundingWidth
h = boundingHeight
break
case 90:
case 270:
w = boundingHeight
h = boundingWidth
break
}
loading = true
imageRender.load(scope.device.display.url +
'?width=' + Math.ceil(w * guestDisplayDensity) +
'&height=' + Math.ceil(h * guestDisplayDensity) +
'&time=' + Date.now()
)
}
}
function on() {
scaler = ScalingService.coordinator(
scope.device.display.width, scope.device.display.height
)
imageRender.onLoad = function (image) {
loading = false
if (scope.$parent.showScreen) {
// Check to set the size only if updated
if (cachedBoundingWidth !== boundingWidth ||
cachedBoundingHeight !== boundingHeight ||
cachedImageWidth !== image.width ||
cachedImageHeight !== image.height ||
cachedRotation !== rotation) {
cachedBoundingWidth = boundingWidth
cachedBoundingHeight = boundingHeight
cachedImageWidth = image.width
cachedImageHeight = image.height
cachedRotation = rotation
imageRender.canvasWidth = cachedImageWidth
imageRender.canvasHeight = cachedImageHeight
var projectedSize = scaler.projectedSize(
boundingWidth, boundingHeight, rotation
)
imageRender.canvasStyleWidth = projectedSize.width
imageRender.canvasStyleHeight = projectedSize.height
// @todo Make sure that each position is able to rotate smoothly
// to the next one. This current setup doesn't work if rotation
// changes from 180 to 270 (it will do a reverse rotation).
switch (rotation) {
case 0:
canvas.style[cssTransform] = 'translate(-50%, -50%) rotate(0deg)'
break
case 90:
canvas.style[cssTransform] = 'translate(-50%, -50%) rotate(-90deg)'
break
case 180:
canvas.style[cssTransform] = 'translate(-50%, -50%) rotate(-180deg)'
break
case 270:
canvas.style[cssTransform] = 'translate(-50%, -50%) rotate(90deg)'
break
}
}
imageRender.draw(image)
// Reset error, if any
if (scope.displayError) {
scope.$apply(function () {
scope.displayError = false
})
}
// Next please
maybeLoadScreen()
}
// Else: Nothing to show
}
imageRender.onError = function (type) {
loading = false
scope.$apply(function () {
if (type === 'timeout') {
scope.displayError = 'timeout'
} else {
scope.displayError = 'secure'
}
})
}
updateBounds()
maybeLoadScreen()
input.bind('keydown', keydownListener)
input.bind('keyup', keyupListener)
input.bind('keypress', keypressListener)
input.bind('paste', pasteListener)
input.bind('copy', copyListener)
if (BrowserInfo.touch) {
element.bind('touchstart', downListener)
} else {
element.bind('mousedown', downListener)
}
}
function off() {
imageRender.onLoad = imageRender.onError = null
loading = false
stopTouch()
input.unbind('keydown', keydownListener)
input.unbind('keyup', keyupListener)
input.unbind('keypress', keypressListener)
input.unbind('paste', pasteListener)
input.unbind('copy', copyListener)
if (BrowserInfo.touch) {
element.unbind('touchstart', downListener)
} else {
element.unbind('mousedown', downListener)
}
}
scope.$watch('$parent.showScreen', function (val) {
if (val) {
maybeLoadScreen()
} else {
scope.fps = null
imageRender.clear()
}
})
function checkEnabled() {
var using = scope.device && scope.device.using
if (using && !PageVisibilityService.hidden) {
on()
}
else {
off()
}
}
scope.$watch('device.using', checkEnabled)
scope.$on('visibilitychange', checkEnabled)
scope.$watch('device.display.rotation', function (r) {
rotation = r || 0
})
scope.$on('guest-portrait', function () {
scope.control.rotate(0)
updateBounds()
})
scope.$on('guest-landscape', function () {
scope.control.rotate(90)
setDisplayDensity(2)
updateBounds()
})
scope.$on('$destroy', off)
}
}
}