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 444a940fbb JSHint fixes.
2014-07-17 18:29:08 +09:00

375 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]
, imageRender = new FastImageRender(canvas, {render: 'canvas', timeout: 3000})
, guestDisplayDensity = setDisplayDensity(1.5)
, guestDisplayRotation = 0
, finger = element.find('span')
, input = element.find('input')
, boundingWidth = 0 // TODO: cache inside FastImageRender?
, boundingHeight = 0
, cachedBoundingWidth = 0
, cachedBoundingHeight = 0
, cachedImageWidth = 0
, cachedImageHeight = 0
, cachedRotation = 0
, rotation = 0
, loading = false
, scaler
, seq = 0
, 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
return guestDisplayDensity = BrowserInfo.mobile && BrowserInfo.retina ? forRetina : 1
}
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) {
loading = true
imageRender.load(scope.device.display.url +
'?width=' + Math.ceil(boundingWidth * guestDisplayDensity) +
'&height=' + Math.ceil(boundingHeight * 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 size = scaler.projectedSize(
boundingWidth
, boundingHeight
, rotation
)
imageRender.canvasStyleWidth = size.width
imageRender.canvasStyleHeight = size.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] = 'rotate(0deg)'
break
case 90:
canvas.style[cssTransform] = 'rotate(-90deg)'
break
case 180:
canvas.style[cssTransform] = 'rotate(-180deg)'
break
case 270:
canvas.style[cssTransform] = '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)
}
}
}