mirror of
https://github.com/sorenisanerd/gotty.git
synced 2024-11-24 04:54:25 +00:00
First, primitive stab at SPiCE integration
Launch an Xspice and run: echo -ne "\033]844;127.0.0.1;9876\007" This will launch a SPiCE client connecting to 127.0.0.1:9876. Still need to add all the security stuff and generally be more defensive in the implementation.
This commit is contained in:
parent
d9fe29e9c7
commit
c66ae7b2e4
9
Makefile
9
Makefile
@ -10,9 +10,9 @@ docker:
|
||||
docker build . -t gotty-bash:$(VERSION)
|
||||
|
||||
.PHONY: asset
|
||||
asset: bindata/static/js/gotty.js bindata/static/index.html bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png server/asset.go
|
||||
asset: bindata/static/js/gotty.js bindata/static/js/spice.js bindata/static/index.html bindata/static/spice bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png server/asset.go
|
||||
|
||||
server/asset.go:
|
||||
server/asset.go: $(shell find bindata)
|
||||
go-bindata -prefix bindata -pkg server -ignore=\\.gitkeep -o server/asset.go bindata/...
|
||||
gofmt -w server/asset.go
|
||||
|
||||
@ -25,6 +25,9 @@ bindata:
|
||||
bindata/static: bindata
|
||||
mkdir bindata/static
|
||||
|
||||
bindata/static/spice:
|
||||
cp -r js/spice-web-client bindata/static/spice
|
||||
|
||||
bindata/static/index.html: bindata/static resources/index.html
|
||||
cp resources/index.html bindata/static/index.html
|
||||
|
||||
@ -56,7 +59,7 @@ js/node_modules/xterm/dist/xterm.css:
|
||||
cd js && \
|
||||
npm install
|
||||
|
||||
bindata/static/js/gotty.js: js/src/* js/node_modules/webpack
|
||||
bindata/static/js/gotty.js: js/src/
|
||||
cd js && \
|
||||
npx webpack
|
||||
|
||||
|
0
js/index.html
Normal file
0
js/index.html
Normal file
22
js/package-lock.json
generated
22
js/package-lock.json
generated
@ -1042,6 +1042,17 @@
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
@ -1360,17 +1371,6 @@
|
||||
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"tapable": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
|
||||
|
8
js/spice-web-client/LICENSE
Normal file
8
js/spice-web-client/LICENSE
Normal file
@ -0,0 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 eyeOS S.L.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
99
js/spice-web-client/README.md
Normal file
99
js/spice-web-client/README.md
Normal file
@ -0,0 +1,99 @@
|
||||
#Complete Spice Web Client written in HTML5 and Javascript
|
||||
Full and complete implementation of the SPICE protocol (by Red Hat) written in HTML5 and JavaScript. It allows any standard HTML5-ready Web Browser to connect to remote
|
||||
virtual sessions just by accessing a single URL.
|
||||
|
||||
The client can be deployed through a normal web server to connect to spice sessions. To use it you would need to proxy your spice session through a websockets-to-tcp
|
||||
proxy like Kanaka, Websockify or similar projects.
|
||||
|
||||
NOTE: This project is NOT based on the spice-html5 prototype.
|
||||
|
||||
## Features
|
||||
|
||||
- Full QXL Support of the entire spice protocol, including clipping, masking, scaling etc (accelerated mode)
|
||||
- Audio support, but only for raw audio samples, not for celt
|
||||
- Full KeyBoard support including English, Spanish and Catalan layouts
|
||||
- Clipboard sharing support with customizble interface
|
||||
- Video streaming support with excellent performance even at 60fps FHD 1080p
|
||||
- Extremly high performant LZ decoder with sub <10ms for a FHD 1080P image
|
||||
- Pure Javascript codec for quic
|
||||
- Configurable multi core support using webworkers (by default it uses 4 CPU Cores)
|
||||
- Spice Agent support
|
||||
- Set resolution support
|
||||
- Honors spice cache for images, cursors and palettes
|
||||
- Very low memory footprint for a javascript application like this
|
||||
- Spice authentication tokens support
|
||||
- Supports graphic live debugging the spice protocol and to replay packets to fix bugs
|
||||
|
||||
##Missing features
|
||||
|
||||
There are some SPICE features still to be implemented, the most important ones are:
|
||||
|
||||
- Celt or other audio codec
|
||||
- USB redirection (not possible at browser level, maybe with a plugin?)
|
||||
|
||||
##Client System requirements
|
||||
|
||||
To get the best result we recommend at least 1GB of ram and at least two cores at 1,5ghz.
|
||||
|
||||
It should work decently on 512mb of ram and 1ghz.
|
||||
|
||||
We have made tests in raspberry pi 2 with very good results.
|
||||
|
||||
##Network requirements
|
||||
|
||||
Only Binary websockets are used to send and receive server data, so you should expect similar network requirements than SPICE itself.
|
||||
for a normal 1080p session the performance is very good up to 150-200ms of latency and 100kb/s bandwidth.
|
||||
|
||||
The network consumption of a spice session depends a lot on the usage patterns.
|
||||
|
||||
##Performance
|
||||
|
||||
Writing a web client for a protocol like spice is challenge because of the limited access to system resources like GPU and the way the javascript VM works.
|
||||
|
||||
We have spent almost 2 years profiling the entire project. The lz decoder has been optimized to <10Ms for full hd images. Quic codec has been hacked a lot
|
||||
to get acceptable performance even being executed in javascript.
|
||||
|
||||
We have created a graphic pipeline to remove unnecesary draw operations that are going to be overdrawn at the next known packets. We have minimized the work
|
||||
for the javascript GC and refined all our canvas operations and all the entire stack to prevent big data structures to be copied.
|
||||
|
||||
You should expect a near perfect experience if you meet the client requirements and the network requirements.
|
||||
|
||||
##Browser support
|
||||
|
||||
We strongly recommend use the spice web client with Chromium/Chrome or Firefox, however it should work at least on:
|
||||
|
||||
- Google Chrome
|
||||
- Firefox
|
||||
- Internet Explorer 11
|
||||
- Edge
|
||||
|
||||
|
||||
##How to use it
|
||||
|
||||
In order to work you only need to provide the IP address of the websockets proxy and the port
|
||||
of the websockets proxy.
|
||||
|
||||
You can do it permanently editing run.js or through the URL using the parameters:
|
||||
|
||||
http://example.com/spice-web-client/index.html?host=IP_ADDRESS_OF_WEBSOCKIFY&port=TCP_PORT_OF_WEBSOCKIFY
|
||||
|
||||
By doing this you will connect to the remote spice session and the resolution will be adapted to your browser viewport area.
|
||||
|
||||
##Notes For linux sessions
|
||||
If you are planning to use this to connect to remote linux sessions you should consider disabling compositing on your desktop. The best performance is achieved with
|
||||
kde with compositing and visual effects disabled.
|
||||
|
||||
Always install the spice-vdagent and xorg-qxl to get the best results and to have custom resolutions etc.
|
||||
|
||||
##Notes For Windows sessions
|
||||
|
||||
Spice web client has a very good performance connecting to remote windows sessions. Always install the spice-agent package including the qxl video driver to get the best results and to have custom resolutions etc.
|
||||
|
||||
##More information
|
||||
|
||||
For more information about the implementation or questions about roadmap etc contact Jose Carlos Norte (jcarlosn) at jcarlos.norte@gmail.com
|
||||
|
||||
##License
|
||||
|
||||
Spice Web Client is distributed under the terms of the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
3363
js/spice-web-client/application/WorkerProcess.js
Normal file
3363
js/spice-web-client/application/WorkerProcess.js
Normal file
File diff suppressed because it is too large
Load Diff
242
js/spice-web-client/application/agent.js
Normal file
242
js/spice-web-client/application/agent.js
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.Agent = $.spcExtend(wdi.EventObject.prototype, {
|
||||
clientTokens:null,
|
||||
serverTokens: 10,
|
||||
app: null,
|
||||
clipboardContent: null,
|
||||
clipboardGrabbed: false,
|
||||
clipboardRequestReceived: false,
|
||||
clipboardPending: false, // to keep clipboard data until spice sends us its request (clipboardRequestReceived)
|
||||
clipboardEnabled: true,
|
||||
windows: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.app = c.app;
|
||||
},
|
||||
|
||||
sendInitMessage: function() {
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_START,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.SpiceMsgMainAgentTokens({
|
||||
num_tokens: this.serverTokens
|
||||
})
|
||||
});
|
||||
this.app.spiceConnection.send(packet);
|
||||
|
||||
var mycaps = (1 << wdi.AgentCaps.VD_AGENT_CAP_MONITORS_CONFIG);
|
||||
if (this.clipboardEnabled) {
|
||||
mycaps = mycaps | (1 << wdi.AgentCaps.VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
|
||||
}
|
||||
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.VDAgentMessage({
|
||||
protocol: 1, //agent protocol version, should be unhardcoded
|
||||
type: wdi.AgentMessageTypes.VD_AGENT_ANNOUNCE_CAPABILITIES,
|
||||
opaque: 0,
|
||||
data: new wdi.VDAgentAnnounceCapabilities({
|
||||
request: 0,
|
||||
caps: mycaps
|
||||
})
|
||||
})
|
||||
});
|
||||
this.sendAgentPacket(packet);
|
||||
|
||||
// //tokens allocation
|
||||
// packet = new wdi.SpiceMessage({
|
||||
// messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_TOKEN,
|
||||
// channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
// args: new wdi.SpiceMsgMainAgentTokens({
|
||||
// num_tokens: 4294967295 // FF FF FF FF
|
||||
// })
|
||||
// });
|
||||
// app.spiceConnection.send(packet);
|
||||
},
|
||||
|
||||
setResolution: function(width, height) {
|
||||
//TODO move this to a setting
|
||||
if(width < 800) {
|
||||
width = 800;
|
||||
}
|
||||
|
||||
if(height < 600) {
|
||||
height = 600;
|
||||
}
|
||||
|
||||
//adapt resolution, TODO: this needs to be refractored
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.VDAgentMessage({
|
||||
protocol: 1, //agent protocol version, should be unhardcoded
|
||||
type: wdi.AgentMessageTypes.VD_AGENT_MONITORS_CONFIG,
|
||||
opaque: 0,
|
||||
data: new wdi.VDAgentMonitorsConfig({
|
||||
num_of_monitors: 1,
|
||||
flags: 0,
|
||||
data: new wdi.VDAgentMonConfig({
|
||||
width: width,
|
||||
height: height,
|
||||
depth: 32,
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
this.sendAgentPacket(packet);
|
||||
},
|
||||
|
||||
setClientTokens: function(tokens) {
|
||||
this.clientTokens = tokens;
|
||||
},
|
||||
|
||||
sendAgentPacket: function(packet) {
|
||||
this.clientTokens--;
|
||||
this.app.spiceConnection.send(packet);
|
||||
},
|
||||
|
||||
onAgentData: function(packet) {
|
||||
this.serverTokens--; //we have just received a server package, we decrement the tokens
|
||||
if (this.serverTokens == 0) { // we send 10 more tokens to server
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_TOKEN,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.SpiceMsgMainAgentTokens({
|
||||
num_tokens: 10
|
||||
})
|
||||
});
|
||||
this.app.spiceConnection.send(packet);
|
||||
this.serverTokens = 10;
|
||||
}
|
||||
|
||||
if(packet.type == wdi.AgentMessageTypes.VD_AGENT_ANNOUNCE_CAPABILITIES) {
|
||||
//??
|
||||
} else if(packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_GRAB) {
|
||||
if(packet.clipboardType == wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT) {
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.VDAgentMessage({
|
||||
protocol: 1, //agent protocol version, should be unhardcoded
|
||||
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_REQUEST,
|
||||
opaque: 0,
|
||||
data: new wdi.VDAgentClipboardRequest({
|
||||
type: wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT
|
||||
})
|
||||
})
|
||||
});
|
||||
this.sendAgentPacket(packet);
|
||||
}
|
||||
} else if(packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD) {
|
||||
this.fire('clipBoardData', packet.clipboardData);
|
||||
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_REQUEST) {
|
||||
this.clipboardRequestReceived = true;
|
||||
if (this.clipboardPending) {
|
||||
this.clipboardPending = false;
|
||||
this.sendPaste();
|
||||
}
|
||||
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_RELEASE) {
|
||||
//debugger;// we've never seen this packet... if we receive it sometime, please warn somebody!!
|
||||
this.clipboardGrabbed = false;
|
||||
this.clipboardRequestReceived = false;
|
||||
} else if (packet.type == wdi.AgentMessageTypes.VD_AGENT_REPLY) {
|
||||
|
||||
} else {
|
||||
console.log('agent ?',packet.type);
|
||||
}
|
||||
},
|
||||
|
||||
setClipboard: function(text) {
|
||||
if (text != this.clipboardContent) {
|
||||
this.clipboardContent = text;
|
||||
this.sendGrab();
|
||||
this.sendPaste();
|
||||
}
|
||||
this.app.sendShortcut(wdi.keyShortcutsHandled.CTRLV);
|
||||
},
|
||||
|
||||
sendGrab: function() {
|
||||
if (!this.clipboardGrabbed) {
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.VDAgentMessage({
|
||||
protocol: 1, //agent protocol version, should be unhardcoded
|
||||
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD_GRAB,
|
||||
opaque: 0,
|
||||
data: new wdi.VDAgentClipboardGrab({
|
||||
types: [wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT]
|
||||
})
|
||||
})
|
||||
});
|
||||
this.sendAgentPacket(packet);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends the text received from browser to spice
|
||||
*
|
||||
* @param clipboardContent
|
||||
*/
|
||||
sendPaste: function() {
|
||||
if (this.clipboardRequestReceived) {
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_AGENT_DATA,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.VDAgentMessage({
|
||||
protocol: 1, //agent protocol version, should be unhardcoded
|
||||
type: wdi.AgentMessageTypes.VD_AGENT_CLIPBOARD,
|
||||
opaque: 0,
|
||||
data: new wdi.VDAgentClipboard({
|
||||
type: wdi.ClipBoardTypes.VD_AGENT_CLIPBOARD_UTF8_TEXT,
|
||||
data: this.clipboardContent
|
||||
})
|
||||
})
|
||||
});
|
||||
this.clipboardRequestReceived = false;
|
||||
this.sendAgentPacket(packet);
|
||||
} else {
|
||||
// we still haven't received the request event from server, we keep the clipboard data until then
|
||||
this.clipboardPending = true;
|
||||
}
|
||||
},
|
||||
|
||||
disableClipboard: function () {
|
||||
this.clipboardEnabled = false;
|
||||
}
|
||||
});
|
483
js/spice-web-client/application/application.js
Normal file
483
js/spice-web-client/application/application.js
Normal file
@ -0,0 +1,483 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
Application = $.spcExtend(wdi.DomainObject, {
|
||||
spiceConnection: null,
|
||||
clientGui: null,
|
||||
agent: null,
|
||||
externalCallback: null,
|
||||
keyboardEnabled: true,
|
||||
packetProcess: null,
|
||||
inputProcess: null,
|
||||
multimediaTime: null,
|
||||
lastMultimediaTime: null,
|
||||
busConnection: null,
|
||||
busProcess: null,
|
||||
timeLapseDetector: null,
|
||||
|
||||
init: function (c) {
|
||||
wdi.GlobalPool.init();
|
||||
this.spiceConnection = c.spiceConnection || new wdi.SpiceConnection();
|
||||
this.clientGui = c.clientGui || new wdi.ClientGui();
|
||||
this.agent = c.agent || new wdi.Agent({
|
||||
app: this
|
||||
});
|
||||
|
||||
this.inputProcess = c.inputProcess || new wdi.InputProcess({
|
||||
clientGui: this.clientGui,
|
||||
spiceConnection: this.spiceConnection
|
||||
});
|
||||
this.packetProcess = c.packetProcess;
|
||||
this.busConnection = c.busConnection || new wdi.BusConnection();
|
||||
this.busProcess = c.busProcess || new wdi.BusProcess({
|
||||
clientGui: this.clientGui,
|
||||
busConnection: this.busConnection
|
||||
});
|
||||
this.timeLapseDetector = c.timeLapseDetector || new wdi.TimeLapseDetector();
|
||||
this.setup();
|
||||
},
|
||||
|
||||
run: function (c) {
|
||||
if(c.hasOwnProperty('seamlessDesktopIntegration')) {
|
||||
wdi.SeamlessIntegration = c['seamlessDesktopIntegration'];
|
||||
}
|
||||
|
||||
if (!this.packetProcess) {
|
||||
var displayProcess = false;
|
||||
|
||||
if (c.useWorkers === false) {
|
||||
displayProcess = new wdi.DisplayProcess({
|
||||
clientGui: this.clientGui
|
||||
});
|
||||
}
|
||||
|
||||
this.packetProcess = new wdi.PacketProcess({
|
||||
app: this,
|
||||
clientGui: this.clientGui,
|
||||
agent: this.agent,
|
||||
spiceConnection: this.spiceConnection,
|
||||
inputsProcess: this.inputProcess,
|
||||
displayProcess: displayProcess
|
||||
})
|
||||
}
|
||||
if (window.vdiLoadTest) {
|
||||
this.spiceConnection.addListener('message', this.onMessage, this);
|
||||
} else {
|
||||
this.spiceConnection.addListener('message', this.packetProcess.process, this.packetProcess);
|
||||
}
|
||||
|
||||
|
||||
this.busConnection.connect(c);
|
||||
this.timeLapseDetector.startTimer();
|
||||
|
||||
if (c['canvasMargin']) {
|
||||
this.clientGui.setCanvasMargin(c['canvasMargin']);
|
||||
}
|
||||
|
||||
if (c['disableClipboard']) {
|
||||
this.agent.disableClipboard();
|
||||
this.clientGui.disableClipboard();
|
||||
this.enableCtrlV();
|
||||
}
|
||||
|
||||
if(c['layer']) {
|
||||
this.clientGui.setLayer(c['layer']);
|
||||
}
|
||||
|
||||
if (this.clientGui.checkFeatures()) {
|
||||
if (wdi.SeamlessIntegration) {
|
||||
this.disableKeyboard();//keyboard should start disabled is integrated
|
||||
}
|
||||
wdi.Keymap.loadKeyMap(c['layout']);
|
||||
this.setExternalCallback(c['callback'], c['context']);
|
||||
|
||||
try {
|
||||
this.connect({
|
||||
host: c['host'],
|
||||
port: c['port'],
|
||||
protocol: c['protocol'],
|
||||
vmHost: c['vmHost'],
|
||||
vmPort: c['vmPort'],
|
||||
vmInfoToken: c['vmInfoToken'],
|
||||
busHost: c['busHost'],
|
||||
token: c['token'],
|
||||
connectionControl: c['connectionControl'],
|
||||
heartbeatToken: c['heartbeatToken'],
|
||||
heartbeatTimeout: c['heartbeatTimeout']
|
||||
});
|
||||
} catch (e) {
|
||||
this.executeExternalCallback('error', 1);
|
||||
}
|
||||
|
||||
this.clientGui.setClientOffset(c['clientOffset']['x'], c['clientOffset']['y']);
|
||||
}
|
||||
if (c.hasOwnProperty('externalClipboardHandling')) {
|
||||
this.externalClipoardHandling = c['externalClipboardHandling'];
|
||||
}
|
||||
},
|
||||
|
||||
end: function () {
|
||||
//TODO: end?
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.spiceConnection.addListener('mouseMode', this.onMouseMode, this);
|
||||
this.spiceConnection.addListener('initAgent', this.onInitAgent, this);
|
||||
this.spiceConnection.addListener('error', this.onDisconnect, this);
|
||||
this.spiceConnection.addListener('channelConnected', this.onChannelConnected, this);
|
||||
this.clientGui.addListener('input', this.onClientInput, this);
|
||||
this.clientGui.addListener('resolution', this.onResolution, this);
|
||||
this.clientGui.addListener('paste', this.onPaste, this);
|
||||
this.clientGui.addListener('startAudio', this.onStartAudio, this);
|
||||
this.busProcess.addListener('windowCreated', this.onWindowCreated, this);
|
||||
this.busProcess.addListener('windowClosed', this.onWindowClosed, this);
|
||||
this.busProcess.addListener('windowMoved', this.onWindowMoved, this);
|
||||
this.busProcess.addListener('windowResized', this.onWindowResized, this);
|
||||
this.busProcess.addListener('windowFocused', this.onWindowFocused, this);
|
||||
this.busProcess.addListener('windowMinimized', this.onWindowMinimized, this);
|
||||
this.busProcess.addListener('windowRestored', this.onWindowRestored, this);
|
||||
this.busProcess.addListener('windowMaximized', this.onWindowMaximized, this);
|
||||
this.busProcess.addListener('busConnected', this.onBusConnected, this);
|
||||
this.busProcess.addListener('menuResponse', this.onMenuResponse, this);
|
||||
this.busProcess.addListener('networkDriveResponse', this.onNetworkDriveResponse, this);
|
||||
this.busProcess.addListener('wrongPathError', this.onWrongPathError, this);
|
||||
this.busProcess.addListener('applicationLaunchedSuccessfully', this.onApplicationLaunchedSuccessfully, this);
|
||||
this.agent.addListener('clipBoardData', this.onClipBoardData, this);
|
||||
this.busConnection.addListener('busMessage', this.onBusMessage, this);
|
||||
this.busConnection.addListener('error', this.onDisconnect, this);
|
||||
this.timeLapseDetector.addListener('timeLapseDetected', this.onTimeLapseDetected, this);
|
||||
},
|
||||
|
||||
onChannelConnected: function(params) {
|
||||
var channel = params;
|
||||
if (channel === wdi.SpiceVars.SPICE_CHANNEL_INPUTS) {
|
||||
this.clientGui.releaseAllKeys();
|
||||
}
|
||||
},
|
||||
|
||||
onNetworkDriveResponse: function(params) {
|
||||
this.executeExternalCallback('networkDriveResponse', params);
|
||||
},
|
||||
|
||||
onDisconnect: function (params) {
|
||||
var error = params;
|
||||
this.executeExternalCallback('error', error);
|
||||
},
|
||||
|
||||
onResolution: function (params) {
|
||||
this.executeExternalCallback('resolution', params);
|
||||
},
|
||||
|
||||
onClipBoardData: function (params) {
|
||||
if (this.externalClipoardHandling) {
|
||||
this.executeExternalCallback('clipboardEvent', params);
|
||||
} else {
|
||||
this.clientGui.setClipBoardData(params);
|
||||
}
|
||||
},
|
||||
|
||||
onWindowMinimized: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.resizeSubCanvas(window);
|
||||
this.executeExternalCallback('windowMinimized', params);
|
||||
},
|
||||
|
||||
onWindowFocused: function (params) {
|
||||
this.executeExternalCallback('windowFocused', params);
|
||||
},
|
||||
|
||||
onWindowRestored: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.resizeSubCanvas(window);
|
||||
this.executeExternalCallback('windowRestored', params);
|
||||
},
|
||||
|
||||
onWindowMaximized: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.resizeSubCanvas(window);
|
||||
this.executeExternalCallback('windowMaximized', params);
|
||||
},
|
||||
|
||||
onWindowResized: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.resizeSubCanvas(window);
|
||||
this.executeExternalCallback('windowResized', params);
|
||||
},
|
||||
|
||||
onWindowMoved: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.moveSubCanvas(window);
|
||||
this.executeExternalCallback('windowMoved', params);
|
||||
},
|
||||
|
||||
onWindowClosed: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.deleteSubCanvas(window);
|
||||
this.executeExternalCallback('windowClosed', params);
|
||||
},
|
||||
|
||||
onWindowCreated: function (params) {
|
||||
var window = params;
|
||||
var params = this.clientGui.createNewSubCanvas(window);
|
||||
this.executeExternalCallback('windowCreated', params);
|
||||
},
|
||||
|
||||
onMenuResponse: function(params) {
|
||||
var menuData = params;
|
||||
this.executeExternalCallback('menuResponse', menuData);
|
||||
},
|
||||
|
||||
//Events
|
||||
onClientInput: function (params) {
|
||||
var data = params;
|
||||
var type = data[0];
|
||||
this.inputProcess.send(data, type);
|
||||
},
|
||||
|
||||
onMessage: function (params) {
|
||||
var message = params;
|
||||
this.packetProcess.process(message);
|
||||
|
||||
var self = this;
|
||||
|
||||
window.checkResultsTimer && clearTimeout(window.checkResultsTimer);
|
||||
window.checkResultsTimer = window.setTimeout(function () {
|
||||
self.executeExternalCallback('checkResults');
|
||||
window.vdiLoadTest = false;
|
||||
}, 5000);
|
||||
|
||||
},
|
||||
|
||||
onBusConnected: function(params) {
|
||||
if (wdi.SeamlessIntegration) {
|
||||
this.busProcess.requestWindowList(); //request windows list
|
||||
}
|
||||
},
|
||||
|
||||
onBusMessage: function (params) {
|
||||
var message = params;
|
||||
this.busProcess.process(message);
|
||||
},
|
||||
|
||||
onInitAgent: function (params) {
|
||||
this.agent.setClientTokens(params);
|
||||
this.agent.sendInitMessage(this);
|
||||
this.executeExternalCallback('ready', params);
|
||||
// this.clientGui.releaseAllKeys();
|
||||
},
|
||||
|
||||
onMouseMode: function (params) {
|
||||
this.clientGui.setMouseMode(params);
|
||||
},
|
||||
|
||||
onPaste: function (params) {
|
||||
this.agent.setClipboard(params);
|
||||
},
|
||||
|
||||
onStartAudio: function () {
|
||||
this.packetProcess.processors[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK].startAudio();
|
||||
},
|
||||
|
||||
onTimeLapseDetected: function (params) {
|
||||
var elapsedMillis = params;
|
||||
this.executeExternalCallback('timeLapseDetected', elapsedMillis);
|
||||
},
|
||||
|
||||
connect: function (connectionInfo) {
|
||||
try {
|
||||
this.spiceConnection.connect(connectionInfo);
|
||||
} catch (e) {
|
||||
this.clientGui.showError(e.message);
|
||||
}
|
||||
},
|
||||
|
||||
setExternalCallback: function (fn, context) {
|
||||
this.externalCallback = [fn, context];
|
||||
},
|
||||
|
||||
executeExternalCallback: function (action, params) {
|
||||
this.externalCallback[0].call(this.externalCallback[1], action, params);
|
||||
},
|
||||
|
||||
sendCommand: function (action, params) {
|
||||
switch (action) {
|
||||
case "close":
|
||||
this.busProcess.closeWindow(params['hwnd']);
|
||||
break;
|
||||
case "move":
|
||||
this.busProcess.moveWindow(params['hwnd'], params['x'], params['y']);
|
||||
break;
|
||||
case "minimize":
|
||||
this.busProcess.minimizeWindow(params['hwnd']);
|
||||
break;
|
||||
case "maximize":
|
||||
this.busProcess.maximizeWindow(params['hwnd']);
|
||||
break;
|
||||
case "restore":
|
||||
this.busProcess.restoreWindow(params['hwnd']);
|
||||
break;
|
||||
case "focus":
|
||||
this.busProcess.focusWindow(params['hwnd']);
|
||||
break;
|
||||
case "resize":
|
||||
this.busProcess.resizeWindow(params['hwnd'], params['width'], params['height']);
|
||||
break;
|
||||
case "run":
|
||||
this.busProcess.executeCommand(params['cmd']);
|
||||
break;
|
||||
case "setResolution":
|
||||
this.agent.setResolution(params['width'], params['height']);
|
||||
break;
|
||||
case 'getMenu':
|
||||
this.busProcess.getMenu();
|
||||
break;
|
||||
case 'reMountNetworkDrive':
|
||||
this.busProcess.reMountNetworkDrive(params['host'], params['username'], params['password']);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
enableKeyboard: function () {
|
||||
this.clientGui.enableKeyboard();
|
||||
},
|
||||
|
||||
disableKeyboard: function () {
|
||||
this.clientGui.disableKeyboard();
|
||||
},
|
||||
|
||||
enableCtrlV: function () {
|
||||
wdi.KeymapES.setCtrlKey(86, 0x2F);
|
||||
wdi.KeymapUS.setCtrlKey(86, 0x2F);
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
this.busConnection.disconnect();
|
||||
this.spiceConnection.disconnect();
|
||||
},
|
||||
|
||||
setMultimediaTime: function (time) {
|
||||
this.multimediaTime = time;
|
||||
this.lastMultimediaTime = Date.now();
|
||||
},
|
||||
|
||||
sendShortcut: function(shortcut) {
|
||||
if(shortcut == wdi.keyShortcutsHandled.CTRLV) {
|
||||
this.inputProcess.send([
|
||||
"keydown",
|
||||
[
|
||||
{
|
||||
'generated': true,
|
||||
'type': "keydown",
|
||||
'keyCode': 17,
|
||||
'charCode': 0
|
||||
}
|
||||
]
|
||||
|
||||
], "keydown"); //ctrl down
|
||||
|
||||
this.inputProcess.send([
|
||||
"keydown",
|
||||
[
|
||||
{
|
||||
'generated': true,
|
||||
'type': "keydown",
|
||||
'keyCode': 86,
|
||||
'charCode': 0
|
||||
}
|
||||
]
|
||||
|
||||
], "keydown"); //v
|
||||
|
||||
this.inputProcess.send([
|
||||
"keyup",
|
||||
[
|
||||
{
|
||||
'generated': true,
|
||||
'type': "keyup",
|
||||
'keyCode': 86,
|
||||
'charCode': 0
|
||||
}
|
||||
]
|
||||
|
||||
], "keyup"); //v up
|
||||
|
||||
this.inputProcess.send([
|
||||
"keyup",
|
||||
[
|
||||
{
|
||||
'generated': true,
|
||||
'type': "keyup",
|
||||
'keyCode': 17,
|
||||
'charCode': 0
|
||||
}
|
||||
]
|
||||
|
||||
], "keyup"); //ctrl up
|
||||
}
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.disableKeyboard();
|
||||
this.disconnect();
|
||||
this.packetProcess.dispose();
|
||||
},
|
||||
|
||||
onWrongPathError: function (params) {
|
||||
this.executeExternalCallback('wrongPathError', params);
|
||||
},
|
||||
|
||||
onApplicationLaunchedSuccessfully: function (params) {
|
||||
this.executeExternalCallback('applicationLaunchedSuccessfully', params);
|
||||
},
|
||||
|
||||
getKeyboardHandler: function() {
|
||||
return this.clientGui.handleKey;
|
||||
},
|
||||
|
||||
getClientGui: function() {
|
||||
return this.clientGui;
|
||||
},
|
||||
|
||||
setCurrentWindow: function(wnd) {
|
||||
this.clientGui.inputManager.setCurrentWindow(wnd);
|
||||
}
|
||||
});
|
||||
|
||||
window['Application'] = Application;
|
||||
Application.prototype['run'] = Application.prototype.run;
|
||||
Application.prototype['sendCommand'] = Application.prototype.sendCommand;
|
||||
Application.prototype['enableKeyboard'] = Application.prototype.enableKeyboard;
|
||||
Application.prototype['disableKeyboard'] = Application.prototype.disableKeyboard;
|
||||
Application.prototype['dispose'] = Application.prototype.dispose;
|
||||
Application.prototype['getKeyboardHandler'] = Application.prototype.getKeyboardHandler;
|
||||
Application.prototype['getClientGui'] = Application.prototype.getClientGui;
|
||||
Application.prototype['setCurrentWindow'] = Application.prototype.setCurrentWindow;
|
639
js/spice-web-client/application/clientgui.js
Normal file
639
js/spice-web-client/application/clientgui.js
Normal file
@ -0,0 +1,639 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SPICE_INPUT_MOTION_ACK_BUNCH = 8;
|
||||
|
||||
wdi.ClientGui = $.spcExtend(wdi.EventObject.prototype, {
|
||||
width: null,
|
||||
height: null,
|
||||
canvas: null,
|
||||
ack_wait: 0,
|
||||
mouse_mode: 0,
|
||||
mouse_status: 0,
|
||||
eventLayer: null,
|
||||
counter: 0,
|
||||
mainCanvas: 0,
|
||||
firstTime: true,
|
||||
clientOffsetX: 0,
|
||||
clientOffsetY: 0,
|
||||
magnifier: null,
|
||||
magnifierBackground: null,
|
||||
firstMove: true,
|
||||
isMagnified: true,
|
||||
isMouseDown: false,
|
||||
soundStarted: false,
|
||||
canvasMarginY: 0,
|
||||
canvasMarginX: 0,
|
||||
stuckKeysHandler: null,
|
||||
|
||||
subCanvas: {},
|
||||
inputManager: null,
|
||||
clipboardEnabled: true,
|
||||
layer: null,
|
||||
|
||||
init: function(c) {
|
||||
this.canvas = {};
|
||||
this.contexts = {};
|
||||
this.superInit();
|
||||
this.magnifier = window.$('<canvas/>').attr({
|
||||
'width': 150,
|
||||
'height': 150
|
||||
}).css({
|
||||
'position': 'absolute',
|
||||
'left': '0px',
|
||||
'top': '0px'
|
||||
});
|
||||
this.stuckKeysHandler = c.stuckKeysHandler || new wdi.StuckKeysHandler();
|
||||
this.stuckKeysHandler.addListener('inputStuck', this._sendInput.bind(this), this);
|
||||
|
||||
//load magnifier background
|
||||
this.magnifierBackground = window.$('<img/>');
|
||||
this.magnifierBackground.attr('src', 'resources/magnifier.png');
|
||||
this.initSound();
|
||||
this.inputManager = c.inputManager || new wdi.InputManager({ stuckKeysHandler: this.stuckKeysHandler, window: $(window)});
|
||||
this.inputManager.setCurrentWindow(window);
|
||||
},
|
||||
|
||||
setLayer: function(layer) {
|
||||
this.layer = layer;
|
||||
},
|
||||
|
||||
disableClipboard: function () {
|
||||
this.clipboardEnabled = false;
|
||||
},
|
||||
|
||||
_sendInput: function (params) {
|
||||
var data = params;
|
||||
var type = data[0];
|
||||
var event = data[1];
|
||||
this.fire('input', [type, event]);
|
||||
},
|
||||
|
||||
releaseAllKeys: function() {
|
||||
this.stuckKeysHandler.releaseAllKeys();
|
||||
},
|
||||
|
||||
getContext: function(surface_id) {
|
||||
return this.contexts[surface_id];
|
||||
},
|
||||
|
||||
getCanvas: function(surface_id) {
|
||||
return this.canvas[surface_id];
|
||||
},
|
||||
|
||||
checkFeatures: function() {
|
||||
if (!Modernizr.canvas || !Modernizr.websockets) {
|
||||
alert('Your Browser is not compatible with WDI. Visit ... for a list of compatible browsers');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
deleteSubCanvas: function(window) {
|
||||
var obj = this.subCanvas[window['hwnd']];
|
||||
this.subCanvas[window['hwnd']] = null;
|
||||
return obj;
|
||||
},
|
||||
|
||||
moveSubCanvas: function(window) {
|
||||
var obj = this.subCanvas[window['hwnd']];
|
||||
obj['info'] = window;
|
||||
this._fillSubCanvasFromWindow(window);
|
||||
return obj;
|
||||
},
|
||||
|
||||
resizeSubCanvas: function(window) {
|
||||
var obj = this.subCanvas[window['hwnd']];
|
||||
$([obj["canvas"], obj["eventLayer"]]).attr({
|
||||
'width': window['width'],
|
||||
'height': window['height']
|
||||
});
|
||||
obj['info'] = window;
|
||||
this._fillSubCanvasFromWindow(window);
|
||||
return obj;
|
||||
},
|
||||
|
||||
_fillSubCanvasFromWindow: function(window) {
|
||||
var top = parseInt(window.top, 10);
|
||||
var left = parseInt(window.left, 10);
|
||||
var width = parseInt(window.width, 10);
|
||||
var height = parseInt(window.height, 10);
|
||||
this.fillSubCanvas({
|
||||
top: top,
|
||||
left: left,
|
||||
right: left + width,
|
||||
bottom: top + height
|
||||
});
|
||||
},
|
||||
|
||||
createNewSubCanvas: function(window) {
|
||||
var evtlayer = this.createEventLayer(window['hwnd'] + '_event', window['width'], window['height']);
|
||||
this.subCanvas[window['hwnd']] = {
|
||||
'canvas': $('<canvas/>').attr({
|
||||
width: window['width'],
|
||||
height: window['height']
|
||||
}).css({
|
||||
display: window['iconic'] ? 'none' : 'block'
|
||||
})[0],
|
||||
'eventLayer': evtlayer,
|
||||
'info': window,
|
||||
'position': 0
|
||||
};
|
||||
//we have the main area drawn?
|
||||
if (this.canvas[this.mainCanvas]) {
|
||||
this._fillSubCanvasFromWindow(window);
|
||||
}
|
||||
return [this.subCanvas[window['hwnd']]];
|
||||
},
|
||||
|
||||
fillSubCanvas: function(filterPosition) {
|
||||
var canvas = this.canvas[this.mainCanvas];
|
||||
var info = null;
|
||||
for (var i in this.subCanvas) {
|
||||
if (this.subCanvas[i] != null && this.subCanvas[i] !== undefined && this.subCanvas.hasOwnProperty(i)) {
|
||||
info = this.subCanvas[i]['info'];
|
||||
if(filterPosition!= null || filterPosition != undefined) {
|
||||
var top = parseInt(info['top'], 10);
|
||||
var left = parseInt(info['left'], 10);
|
||||
var width = parseInt(info['width'], 10);
|
||||
var height = parseInt(info['height'], 10);
|
||||
var position = {
|
||||
top: top,
|
||||
left: left,
|
||||
right: left + width,
|
||||
bottom: top + height
|
||||
};
|
||||
if (wdi.CollisionDetector.thereIsBoxCollision(position, filterPosition)) {
|
||||
this._doDrawSubCanvas(canvas, this.subCanvas[i], info);
|
||||
}
|
||||
} else {
|
||||
this._doDrawSubCanvas(canvas, this.subCanvas[i], info);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_doDrawSubCanvas: function(canvas, subCanvas, info) {
|
||||
if(this.canvas[this.mainCanvas] == null || this.canvas[this.mainCanvas] == undefined) {
|
||||
return;
|
||||
}
|
||||
var destCtx = null;
|
||||
if (info['iconic'] === 0) {
|
||||
var destCanvas = subCanvas['canvas'];
|
||||
destCtx = destCanvas.getContext("2d");
|
||||
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var width = +info.width;
|
||||
var height = +info.height;
|
||||
var left = +info['left'];
|
||||
var top = +info['top'];
|
||||
|
||||
if (left < 0) {
|
||||
width = width + left;
|
||||
x = -left;
|
||||
left = 0;
|
||||
}
|
||||
|
||||
if (top < 0) {
|
||||
height = height + top;
|
||||
y = -top;
|
||||
top = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// if width or height are less than 1 or a float
|
||||
// drawImage fails in firefox (ERROR: IndexSizeError)
|
||||
width = Math.max(1, Math.floor(width));
|
||||
height = Math.max(1, Math.floor(height));
|
||||
if (width > canvas.width) width = canvas.width;
|
||||
if (height > canvas.height) height = canvas.height;
|
||||
destCtx.drawImage(canvas, left, top, width, height, x, y, width, height);
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
removeCanvas: function(spiceMessage) {
|
||||
var surface = spiceMessage.args;
|
||||
if (surface.surface_id === this.mainCanvas) {
|
||||
$(this.eventLayer).remove();
|
||||
this.eventLayer = null;
|
||||
}
|
||||
|
||||
this.canvas[surface.surface_id].keepAlive = false;
|
||||
delete this.canvas[surface.surface_id];
|
||||
delete this.contexts[surface.surface_id];
|
||||
},
|
||||
|
||||
drawCanvas: function(spiceMessage) {
|
||||
var surface = spiceMessage.args;
|
||||
var cnv = wdi.GlobalPool.create('Canvas');
|
||||
cnv.keepAlive = true; //prevent this canvas to return to the pool by packetfilter
|
||||
|
||||
cnv.id = 'canvas_' + surface.surface_id;
|
||||
cnv.width = surface.width;
|
||||
cnv.height = surface.height;
|
||||
cnv.style.position = 'absolute';
|
||||
cnv.style.top = this.canvasMarginY + 'px';
|
||||
cnv.style.left = this.canvasMarginX + 'px';
|
||||
|
||||
this.canvas[surface.surface_id] = cnv;
|
||||
this.contexts[surface.surface_id] = cnv.getContext('2d');
|
||||
|
||||
if (surface.flags && !wdi.SeamlessIntegration) {
|
||||
this.mainCanvas = surface.surface_id;
|
||||
|
||||
this.eventLayer = this.createEventLayer('eventLayer', surface.width, surface.height);
|
||||
|
||||
var evLayer = $(this.eventLayer).css({
|
||||
position: 'absolute',
|
||||
top: this.canvasMarginY + 'px',
|
||||
left: this.canvasMarginX + 'px'
|
||||
})[0];
|
||||
|
||||
if(this.layer) {
|
||||
this.layer.appendChild(cnv);
|
||||
this.layer.appendChild(evLayer);
|
||||
} else {
|
||||
document.body.appendChild(cnv);
|
||||
document.body.appendChild(evLayer);
|
||||
}
|
||||
|
||||
this.enableKeyboard();
|
||||
}
|
||||
|
||||
//this goes here?
|
||||
if (this.firstTime && this.clipboardEnabled) {
|
||||
var self = this;
|
||||
$(document).bind('paste', function(event) {
|
||||
self.fire('paste', event.originalEvent.clipboardData.getData('text/plain'));
|
||||
});
|
||||
this.firstTime = false;
|
||||
}
|
||||
|
||||
|
||||
//notify about resolution
|
||||
if (surface.flags) {
|
||||
this.fire('resolution', [this.canvas[surface.surface_id].width, this.canvas[surface.surface_id].height]);
|
||||
}
|
||||
},
|
||||
|
||||
disableKeyboard: function() {
|
||||
var documentDOM = window.$(window.document);
|
||||
documentDOM.unbind('keydown', this.handleKey);
|
||||
documentDOM.unbind('keyup', this.handleKey);
|
||||
documentDOM.unbind('keypress', this.handleKey);
|
||||
this.inputManager.disable();
|
||||
},
|
||||
|
||||
enableKeyboard: function() {
|
||||
var self = this,
|
||||
documentDOM = window.$(window.document);
|
||||
documentDOM['keydown']([self], this.handleKey);
|
||||
documentDOM['keypress']([self], this.handleKey);
|
||||
documentDOM['keyup']([self], this.handleKey);
|
||||
this.inputManager.enable();
|
||||
},
|
||||
|
||||
setCanvasMargin: function(canvasMargin) {
|
||||
this.canvasMarginX = canvasMargin.x;
|
||||
this.canvasMarginY = canvasMargin.y;
|
||||
},
|
||||
|
||||
createEventLayer: function(event_id, width, height) {
|
||||
var self = this;
|
||||
|
||||
var eventLayer = $('<canvas/>').css({
|
||||
cursor: 'default',
|
||||
position: 'absolute'
|
||||
}).attr({
|
||||
id: event_id,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
if (window['bowser']['firefox']) {
|
||||
eventLayer.attr('contentEditable', true);
|
||||
}
|
||||
|
||||
eventLayer.bind('touchstart', function(event) {
|
||||
event.preventDefault();
|
||||
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
|
||||
var x = touch.pageX;
|
||||
var y = touch.pageY;
|
||||
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY, self.mouse_status]);
|
||||
if (event.originalEvent.touches.length === 1) {
|
||||
self.enabledTouchMove = true;
|
||||
self.launchRightClick.call(self, x, y);
|
||||
} else if (event.originalEvent.touches.length === 2) {
|
||||
self.touchX = x;
|
||||
self.touchY = y;
|
||||
self.enabledTouchMove = false;
|
||||
} else if (event.originalEvent.touches.length === 3) {
|
||||
self.touchY3 = y;
|
||||
self.enabledTouchMove = false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
eventLayer.bind('touchmove', function(event) {
|
||||
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
|
||||
var x = touch.pageX;
|
||||
var y = touch.pageY;
|
||||
//TODO: ignore first move
|
||||
if (event.originalEvent.touches.length === 1 && self.enabledTouchMove) {
|
||||
self.isMagnified = true; //magnified!
|
||||
clearInterval(self.rightClickTimer); //cancel, this is not a right click
|
||||
|
||||
if (!self.isMouseDown) {
|
||||
clearInterval(self.mouseDownTimer); //cancel, not enough time to send mousedown
|
||||
self.launchMouseDown(); //fire again
|
||||
}
|
||||
|
||||
|
||||
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY - 80, self.mouse_status]);
|
||||
var pos = $(this).offset();
|
||||
var myX = x - pos.left;
|
||||
var myY = y - pos.top;
|
||||
|
||||
//draw magnifier
|
||||
if (self.firstMove) {
|
||||
$('body').append(self.magnifier);//TODO: append to body?
|
||||
self.firstMove = false;
|
||||
}
|
||||
|
||||
var posX = myX - 75;
|
||||
var posY = myY - 160;
|
||||
|
||||
self.magnifier.css({
|
||||
'left': posX,
|
||||
'top': posY
|
||||
});
|
||||
|
||||
//fill magnifier
|
||||
var ctx = self.magnifier[0].getContext('2d');
|
||||
ctx.clearRect(0, 0, 150, 150);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(75, 75, 75, 0, 2 * Math.PI, false);
|
||||
ctx.clip();
|
||||
ctx.drawImage(
|
||||
self.getCanvas(0),
|
||||
myX - 50, //-50 because we are going to get
|
||||
myY - 50 - 80, //100 px and we want the finder to be the center
|
||||
//-80 becasue the magnifier is 160px up (160/2)
|
||||
//we need to clean all this after the demo
|
||||
//is working
|
||||
100,
|
||||
100,
|
||||
0,
|
||||
0,
|
||||
150,
|
||||
150
|
||||
);
|
||||
// //draw the background
|
||||
ctx.drawImage(self.magnifierBackground[0], 0, 0);
|
||||
ctx.restore();
|
||||
//empty magnifier
|
||||
} else if (event.originalEvent.touches.length === 2) {
|
||||
var delta = self.touchY - y;
|
||||
if (Math.abs(delta) > 10) {
|
||||
var button = delta > 0 ? 4 : 3;
|
||||
self.touchX = x;
|
||||
self.touchY = y;
|
||||
self.generateEvent.call(self, 'mousedown', button);
|
||||
self.generateEvent.call(self, 'mouseup', button);
|
||||
}
|
||||
} else if (event.originalEvent.touches.length === 3) {
|
||||
var delta = self.touchY3 - y;
|
||||
if (delta > 100) {
|
||||
document.getElementById('hiddeninput').select();
|
||||
}
|
||||
}
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
eventLayer.bind('touchend', function(event) {
|
||||
if (self.enabledTouchMove) {
|
||||
var touch = event.originalEvent.touches[0] || event.originalEvent.changedTouches[0];
|
||||
var x = touch.pageX;
|
||||
var y = touch.pageY;
|
||||
if (!self.isMouseDown) {
|
||||
self.generateEvent.call(self, 'mousedown', 0);
|
||||
}
|
||||
self.isMouseDown = false;
|
||||
self.generateEvent.call(self, 'mouseup', 0);
|
||||
var pos = $(this).offset();
|
||||
|
||||
self.enabledTouchMove = false;
|
||||
self.firstMove = true;
|
||||
if (self.isMagnified) {
|
||||
self.magnifier.remove();
|
||||
}
|
||||
self.isMagnified = false;
|
||||
}
|
||||
clearInterval(self.rightClickTimer); //cancel, this is not a right click
|
||||
clearInterval(self.mouseDownTimer); //cancel
|
||||
});
|
||||
|
||||
//if (!Modernizr.touch) {
|
||||
eventLayer['mouseup'](function(event) {
|
||||
var button = event.button;
|
||||
|
||||
self.generateEvent.call(self, 'mouseup', button);
|
||||
self.mouse_status = 0;
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
eventLayer['mousedown'](function(event) {
|
||||
var button = event.button;
|
||||
|
||||
self.generateEvent.call(self, 'mousedown', button);
|
||||
self.mouse_status = 1;
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
eventLayer['mousemove'](function(event) {
|
||||
var x = event.pageX;
|
||||
var y = event.pageY;
|
||||
self.generateEvent.call(self, 'mousemove', [x + self.clientOffsetX, y + self.clientOffsetY, self.mouse_status]);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
eventLayer.bind('contextmenu', function(event) {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
//}
|
||||
|
||||
var mouseEventPause = false;
|
||||
|
||||
eventLayer.bind('mousewheel', function(event, delta) {
|
||||
var button = delta > 0 ? 3 : 4;
|
||||
|
||||
self.generateEvent.call(self, 'mousedown', button);
|
||||
self.generateEvent.call(self, 'mouseup', button);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
wdi.VirtualMouse.setEventLayer(eventLayer[0], 0, 0, width, height, 1);
|
||||
return eventLayer[0];
|
||||
},
|
||||
|
||||
launchRightClick: function(x, y) {
|
||||
var self = this;
|
||||
this.rightClickTimer = setTimeout(function() {
|
||||
self.generateEvent.call(self, 'mousedown', 2);
|
||||
self.generateEvent.call(self, 'mouseup', 2);
|
||||
self.enabledTouchMove = false;
|
||||
}, 400);
|
||||
},
|
||||
|
||||
launchMouseDown: function(x, y) {
|
||||
var self = this;
|
||||
this.mouseDownTimer = setTimeout(function() {
|
||||
self.isMouseDown = true;
|
||||
self.generateEvent.call(self, 'mousedown', 0);
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
showError: function(message) {
|
||||
wdi.Debug.warn(message);
|
||||
$('<div/>', {
|
||||
id: 'error'
|
||||
}).html(message).css({
|
||||
'background-color': '#ff4141'
|
||||
}).appendTo('body');
|
||||
|
||||
setTimeout("$('#error').remove()", 2000);
|
||||
},
|
||||
|
||||
generateEvent: function(event, params) {
|
||||
if (event === 'mousemove' || event === 'joystick') {
|
||||
if (this.ack_wait < wdi.SPICE_INPUT_MOTION_ACK_BUNCH) {
|
||||
this.ack_wait++;
|
||||
this.fire('input', [event, params]);
|
||||
}
|
||||
} else {
|
||||
if (event.indexOf('key') > -1) { // it's a keyEvent
|
||||
this.stuckKeysHandler.checkSpecialKey(event, params[0]['keyCode']);
|
||||
var val = this.inputManager.getValue();
|
||||
if (val) {
|
||||
params = this.inputManager.manageChar(val, params);
|
||||
}
|
||||
}
|
||||
this.fire('input', [event, params]);
|
||||
}
|
||||
},
|
||||
|
||||
motion_ack: function() {
|
||||
this.ack_wait = 0;
|
||||
},
|
||||
|
||||
setMouseMode: function(mode) {
|
||||
this.mouse_mode = mode;
|
||||
},
|
||||
|
||||
handleKey: function(e) {
|
||||
e.data[0].generateEvent.call(e.data[0], e.type, [e]);
|
||||
|
||||
if (wdi.Keymap.isInKeymap(e.keyCode) && e.type !== "keypress") {
|
||||
e.preventDefault();
|
||||
}
|
||||
//e.data[0].stuckKeysHandler.handleStuckKeys(e);
|
||||
},
|
||||
|
||||
setClientOffset: function(x, y) {
|
||||
this.clientOffsetX = x;
|
||||
this.clientOffsetY = y;
|
||||
},
|
||||
|
||||
setClipBoardData: function(data) {
|
||||
//we have received new clipboard data
|
||||
//show to the user
|
||||
//TODO: create a new dialog with buttons to copy the data directly
|
||||
//from the textbox
|
||||
prompt("New clipboard data available, press ctrl+c to copy it", data);
|
||||
},
|
||||
|
||||
initSound: function() {
|
||||
var self = this;
|
||||
// if (!Modernizr.touch) {
|
||||
this.soundStarted = true;
|
||||
window.setTimeout(function() {
|
||||
self.fire('startAudio');
|
||||
}, 100);
|
||||
/* } else {
|
||||
var $button = $('<button>Start</button>', {id: "startAudio"}).css({
|
||||
padding: "10px 25px",
|
||||
fontSize: "25px",
|
||||
fontFamily: "Verdana",
|
||||
cursor: "pointer",
|
||||
margin: "0 auto"
|
||||
}).click(function() {
|
||||
self.soundStarted = true;
|
||||
self.fire('startAudio');
|
||||
$('#soundButtonContainer').remove();
|
||||
});
|
||||
|
||||
var $messageContainer = $('<div id="messageContainer"><p>Click to start using your virtual session:</p></div>').css({
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
fontSize: "25px",
|
||||
fontFamily: "Verdana",
|
||||
marginTop: "75px"
|
||||
});
|
||||
|
||||
var $container = $('<div></div>', {id: "soundButtonContainer"});
|
||||
|
||||
$button.appendTo($messageContainer);
|
||||
$messageContainer.appendTo($container);
|
||||
$container.appendTo('body');
|
||||
|
||||
$container.css({
|
||||
position: 'absolute',
|
||||
zIndex: 999999999,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: document.height,
|
||||
backgroundColor: "black"
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
});
|
87
js/spice-web-client/application/imagecache.js
Normal file
87
js/spice-web-client/application/imagecache.js
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.ImageCache = {
|
||||
images: {},
|
||||
cursor: {},
|
||||
palettes: {},
|
||||
|
||||
getImageFrom: function(descriptor, cb) {
|
||||
//see http://jsperf.com/todataurl-vs-getimagedata-to-base64/7
|
||||
var cnv = wdi.GlobalPool.create('Canvas');
|
||||
var imgData = this.images[descriptor.id.toString()];
|
||||
cnv.width = imgData.width;
|
||||
cnv.height = imgData.height;
|
||||
cnv.getContext('2d').putImageData(imgData,0,0);
|
||||
cb(cnv);
|
||||
},
|
||||
|
||||
isImageInCache: function(descriptor) {
|
||||
if(descriptor.id.toString() in this.images) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
delImage: function(id) {
|
||||
delete this.images[id.toString()];
|
||||
},
|
||||
|
||||
addImage: function(descriptor, canvas) {
|
||||
if(canvas.getContext) {
|
||||
this.images[descriptor.id.toString()] = canvas.getContext('2d').getImageData(0,0,canvas.width, canvas.height);
|
||||
} else {
|
||||
this.images[descriptor.id.toString()] = canvas;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
getCursorFrom: function(cursor) {
|
||||
return this.cursor[cursor.header.unique.toString()];
|
||||
},
|
||||
|
||||
addCursor: function(cursor, imageData) {
|
||||
this.cursor[cursor.header.unique.toString()] = imageData;
|
||||
},
|
||||
|
||||
getPalette: function(id) {
|
||||
return this.palettes[id.toString()];
|
||||
},
|
||||
|
||||
addPalette: function(id, palette) {
|
||||
this.palettes[id.toString()] = palette;
|
||||
},
|
||||
|
||||
clearPalettes: function() {
|
||||
this.palettes = {};
|
||||
}
|
||||
};
|
121
js/spice-web-client/application/inputmanager.js
Normal file
121
js/spice-web-client/application/inputmanager.js
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.InputManager = $.spcExtend(wdi.EventObject.prototype, {
|
||||
|
||||
checkFocus: false,
|
||||
input: null,
|
||||
window: null,
|
||||
stuckKeysHandler: null,
|
||||
|
||||
init: function (c) {
|
||||
this.superInit();
|
||||
this.input = c.input;
|
||||
this.window = c.window;
|
||||
this.stuckKeysHandler = c.stuckKeysHandler;
|
||||
this.$ = c.jQuery || $;
|
||||
if (!c.disableInput) {
|
||||
this.inputElement = this.$('<div style="position:absolute"><input type="text" id="inputmanager" style="opacity:0;color:transparent"/></div>');
|
||||
}
|
||||
this.currentWindow = null;
|
||||
},
|
||||
|
||||
setCurrentWindow: function(wnd) {
|
||||
wnd = this.$(wnd);
|
||||
if(this.currentWindow) {
|
||||
this.inputElement.remove();
|
||||
//remove listeners
|
||||
this.currentWindow.unbind('blur');
|
||||
}
|
||||
this.$(wnd[0].document.body).prepend(this.inputElement);
|
||||
this.input = this.$(wnd[0].document.getElementById('inputmanager'));
|
||||
//TODO: remove events from the other window
|
||||
this.addListeners(wnd);
|
||||
this.currentWindow = wnd;
|
||||
},
|
||||
|
||||
addListeners: function (wnd) {
|
||||
this._onBlur(wnd);
|
||||
this._onInput();
|
||||
},
|
||||
|
||||
_onBlur: function (wnd) {
|
||||
var self = this;
|
||||
wnd.on('blur', function onBlur (e) {
|
||||
if (self.checkFocus) {
|
||||
self.input.focus();
|
||||
}
|
||||
self.stuckKeysHandler.releaseSpecialKeysPressed();
|
||||
});
|
||||
},
|
||||
|
||||
_onInput: function () {
|
||||
var self = this;
|
||||
this.input.on('input', function input (e) {
|
||||
// ctrl-v issue related
|
||||
var aux = self.input.val();
|
||||
if (aux.length > 1) {
|
||||
self.reset();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
this.checkFocus = true;
|
||||
this.input.select();
|
||||
},
|
||||
|
||||
disable: function () {
|
||||
this.checkFocus = false;
|
||||
this.input.blur();
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
this.input.val("");
|
||||
},
|
||||
|
||||
getValue: function () {
|
||||
var val = this.input.val();
|
||||
if (val) {
|
||||
this.reset();
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
manageChar: function (val, params) {
|
||||
var res = [Object.create(params[0])];
|
||||
res[0]['type'] = 'inputmanager';
|
||||
res[0]['charCode'] = val.charCodeAt(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
});
|
190
js/spice-web-client/application/packetfactory.js
Normal file
190
js/spice-web-client/application/packetfactory.js
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketFactory = {
|
||||
extract: function(rawSpiceMessage) {
|
||||
var packet = null;
|
||||
switch (rawSpiceMessage.channel) {
|
||||
case wdi.SpiceVars.SPICE_CHANNEL_DISPLAY:
|
||||
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
|
||||
var originalData = JSON.stringify(rawSpiceMessage);
|
||||
}
|
||||
switch (rawSpiceMessage.header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_MODE:
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK:
|
||||
packet = new wdi.SpiceDisplayMark().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET:
|
||||
packet = new wdi.SpiceDisplayReset().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS:
|
||||
packet = new wdi.SpiceCopyBits().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST:
|
||||
packet = new wdi.SpiceResourceList().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS:
|
||||
//TODO: remove all pixmaps
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_PALETTE:
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES:
|
||||
packet = new wdi.SpiceDisplayInvalidAllPalettes().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE:
|
||||
packet = new wdi.SpiceStreamCreate().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA:
|
||||
packet = new wdi.SpiceStreamData().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP:
|
||||
packet = new wdi.SpiceStreamClip().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY:
|
||||
packet = new wdi.SpiceStreamDestroy().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL:
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
|
||||
packet = new wdi.SpiceDrawFill().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_OPAQUE:
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
|
||||
// Spice Draw Copy is composed by DisplayBase (surface_id 32, SpiceRect(top 32, left 32, bottom 32, right 32), SpiceClip(type 8 if 1: SpiceClipRects(num_rects 32, vector: SpiceRect(top 32, left 32, bottom 32, right 32)))) and SpiceCopy (offset 32 if not 0: SpiceImage(SpiceImageDescriptor(id 32, type 8, flags 8, width 32, height 32), case descriptor type to parse image), SpiceRect(top 32, left 32, bottom 32, right 32), rop_descriptor 16, scale_mode 8, SpiceQMask)
|
||||
packet = new wdi.SpiceDrawCopy().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
|
||||
packet = new wdi.drawBlend().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS:
|
||||
packet = new wdi.SpiceDrawBlackness().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS:
|
||||
packet = new wdi.SpiceDrawWhiteness().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS:
|
||||
packet = new wdi.SpiceDrawInvers().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3:
|
||||
packet = new wdi.SpiceDrawRop3().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE:
|
||||
packet = new wdi.SpiceStroke().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT:
|
||||
packet = new wdi.SpiceDrawText().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
|
||||
packet = new wdi.drawTransparent().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
|
||||
packet = new wdi.drawAlphaBlend().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE:
|
||||
packet = new wdi.SpiceSurface().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY:
|
||||
packet = new wdi.SpiceSurfaceDestroy().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_CHANNEL_INPUTS:
|
||||
switch (rawSpiceMessage.header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
|
||||
packet = new Object(); //dummy!
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_CHANNEL_MAIN:
|
||||
switch (rawSpiceMessage.header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_INIT:
|
||||
packet = new wdi.RedMainInit().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DATA:
|
||||
packet = new wdi.VDAgentMessage().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DISCONNECTED:
|
||||
packet = new wdi.SpiceMsgMainAgentDisconnected().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_CONNECTED:
|
||||
packet = new wdi.SpiceMsgMainAgentConnected().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
|
||||
packet = new wdi.MainMultiMediaTime().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_CHANNELS_LIST:
|
||||
packet = new wdi.MainMChannelsList().demarshall(rawSpiceMessage.body);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_CHANNEL_CURSOR:
|
||||
switch (rawSpiceMessage.header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_CURSOR_INIT:
|
||||
packet = new wdi.RedCursorInit().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_CURSOR_SET:
|
||||
packet = new wdi.RedCursorSet().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK:
|
||||
switch(rawSpiceMessage.header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_MODE:
|
||||
packet = new wdi.PlaybackMode().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_START:
|
||||
packet = new wdi.PlaybackStart().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_STOP:
|
||||
packet = new wdi.PlaybackStop().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_DATA:
|
||||
packet = new wdi.PlaybackData().demarshall(rawSpiceMessage.body, rawSpiceMessage.header.size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(packet) {
|
||||
if (wdi.graphicDebug && wdi.graphicDebug.debugMode && originalData) {
|
||||
packet.originalData = originalData;
|
||||
}
|
||||
return new wdi.SpiceMessage({
|
||||
messageType: rawSpiceMessage.header.type,
|
||||
channel: rawSpiceMessage.channel,
|
||||
args: packet
|
||||
});
|
||||
}
|
||||
wdi.Debug.log(rawSpiceMessage.header.type, rawSpiceMessage.header.channel);
|
||||
return false;
|
||||
}
|
||||
};
|
110
js/spice-web-client/application/packetfilter.js
Normal file
110
js/spice-web-client/application/packetfilter.js
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketFilter = {
|
||||
restoreContext: false,
|
||||
start: null,
|
||||
filter: function(spiceMessage, fn, scope, clientGui) {
|
||||
if(wdi.logOperations) {
|
||||
this.start = Date.now();
|
||||
}
|
||||
|
||||
//TODO: design an architecture for loading
|
||||
//dynamic filters, instead of filtering here.
|
||||
//This should be just the entry point for filters.
|
||||
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
|
||||
wdi.graphicDebug.printDebugMessageOnFilter(spiceMessage, clientGui);
|
||||
}
|
||||
//end of hardcoded filter
|
||||
|
||||
// MS Word Benchmark startup
|
||||
if (wdi.IntegrationBenchmark && wdi.IntegrationBenchmark.benchmarking) {
|
||||
var date = new Date();
|
||||
wdi.IntegrationBenchmark.setStartTime(date.getTime());
|
||||
}
|
||||
|
||||
//check clipping
|
||||
if(spiceMessage.args.base) {
|
||||
if(spiceMessage.args.base.clip.type === wdi.SpiceClipType.SPICE_CLIP_TYPE_RECTS) {
|
||||
var context = clientGui.getContext(spiceMessage.args.base.surface_id);
|
||||
context.save();
|
||||
context.beginPath();
|
||||
var rects = spiceMessage.args.base.clip.rects.rects;
|
||||
var len = rects.length;
|
||||
while(len--) {
|
||||
var box = wdi.graphics.getBoxFromSrcArea(rects[len]);
|
||||
context.rect(box.x, box.y, box.width, box.height);
|
||||
}
|
||||
context.clip();
|
||||
this.restoreContext = spiceMessage.args.base.surface_id;
|
||||
}
|
||||
}
|
||||
fn.call(scope, spiceMessage);
|
||||
},
|
||||
|
||||
notifyEnd: function(spiceMessage, clientGui) {
|
||||
if(this.restoreContext !== false) {
|
||||
var context = clientGui.getContext(this.restoreContext);
|
||||
context.restore();
|
||||
this.restoreContext = false;
|
||||
}
|
||||
|
||||
if(wdi.SeamlessIntegration) {
|
||||
var filterPosition = null;
|
||||
if(spiceMessage.args.base && spiceMessage.args.base.box) {
|
||||
filterPosition = spiceMessage.args.base.box;
|
||||
}
|
||||
clientGui.fillSubCanvas(filterPosition);
|
||||
}
|
||||
|
||||
if (wdi.graphicDebug && wdi.graphicDebug.debugMode) {
|
||||
wdi.graphicDebug.printDebugMessageOnNotifyEnd(spiceMessage, clientGui);
|
||||
}
|
||||
|
||||
// MS Word Benchmark
|
||||
if (wdi.IntegrationBenchmark && wdi.IntegrationBenchmark.benchmarking) {
|
||||
var date = new Date();
|
||||
wdi.IntegrationBenchmark.setEndTime(date.getTime());
|
||||
}
|
||||
|
||||
// clear the tmpcanvas
|
||||
wdi.GlobalPool.cleanPool('Canvas');
|
||||
wdi.GlobalPool.cleanPool('Image');
|
||||
if(wdi.logOperations) {
|
||||
wdi.DataLogger.log(spiceMessage, this.start);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
80
js/spice-web-client/application/packetprocess.js
Normal file
80
js/spice-web-client/application/packetprocess.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketProcess = $.spcExtend(wdi.DomainObject, {
|
||||
processors: {},
|
||||
|
||||
init: function(c) {
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_MAIN] = c.mainProcess || new wdi.MainProcess({
|
||||
app: c.app
|
||||
});
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY] = c.displayProcess || new wdi.DisplayPreProcess({
|
||||
clientGui: c.clientGui
|
||||
});
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_INPUTS] = c.inputsProcess || new wdi.InputProcess({
|
||||
clientGui: c.clientGui,
|
||||
spiceConnection: c.spiceConnection
|
||||
});
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_CURSOR] = c.cursorProcess || new wdi.CursorProcess();
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK] = c.playbackProcess || new wdi.PlaybackProcess({
|
||||
app: c.app
|
||||
});
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
if(wdi.exceptionHandling) {
|
||||
return this.processExceptionHandled(spiceMessage);
|
||||
} else {
|
||||
return this.processPacket(spiceMessage);
|
||||
}
|
||||
},
|
||||
|
||||
processExceptionHandled: function(spiceMessage) {
|
||||
try {
|
||||
return this.processPacket(spiceMessage);
|
||||
} catch(e) {
|
||||
wdi.Debug.error('PacketProcess: Error processing packet', e);
|
||||
}
|
||||
},
|
||||
|
||||
processPacket: function(spiceMessage) {
|
||||
if(!spiceMessage || !this.processors[spiceMessage.channel]) {
|
||||
throw "Invalid channel or null message";
|
||||
}
|
||||
|
||||
this.processors[spiceMessage.channel].process(spiceMessage);
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.processors[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY].dispose();
|
||||
}
|
||||
});
|
129
js/spice-web-client/application/rasteroperation.js
Normal file
129
js/spice-web-client/application/rasteroperation.js
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.RasterOperation = {
|
||||
|
||||
process: function(rop, sourceImg, destImg) {//sourceImg could be brush or image (both imageData)
|
||||
var result = null;
|
||||
if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_SRC) {
|
||||
sourceImg = this.invert(sourceImg);
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_BRUSH) {
|
||||
sourceImg = this.invert(sourceImg);
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_DEST) {
|
||||
destImg = this.invert(destImg);
|
||||
}
|
||||
|
||||
if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
|
||||
return sourceImg;
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_OR) {
|
||||
result = this.boolOp(sourceImg, destImg, 'or');
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_AND) {
|
||||
result = this.boolOp(sourceImg, destImg, 'and');
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_XOR) {
|
||||
result = this.boolOp(sourceImg, destImg, 'xor');
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_BLACKNESS) {
|
||||
result = this.lightness(destImg, 'b');
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_WHITENESS) {
|
||||
result = this.lightness(destImg);
|
||||
} else if (rop & wdi.SpiceRopd.SPICE_ROPD_OP_INVERS) {
|
||||
result = this.invert(destImg);
|
||||
}
|
||||
|
||||
if (rop & wdi.SpiceRopd.SPICE_ROPD_INVERS_RES) {
|
||||
return this.invert(result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
flip: function(sourceImg) {
|
||||
sourceImg = wdi.Flipper.flip(sourceImg);
|
||||
return sourceImg;
|
||||
},
|
||||
|
||||
invert: function(sourceImg) {
|
||||
sourceImg = $(sourceImg).pixastic('invert')[0];
|
||||
|
||||
return sourceImg;
|
||||
},
|
||||
|
||||
lightness: function(sourceImg, ratio) {
|
||||
var ratio = ratio==='b'?-100:100;
|
||||
sourceImg = $(sourceImg).pixastic('hsl', {hue:30,saturation:20,lightness:ratio})[0];
|
||||
|
||||
return sourceImg;
|
||||
},
|
||||
|
||||
boolOp: function(sourceImg, destImg, op) {
|
||||
//or and and xor implemented without globalcomposition
|
||||
//because it is really buggy
|
||||
|
||||
var source = wdi.graphics.getDataFromImage(sourceImg).data;
|
||||
var dest = wdi.graphics.getDataFromImage(destImg).data;
|
||||
|
||||
var length = source.length-1;
|
||||
var tmp_canvas = wdi.graphics.getNewTmpCanvas(sourceImg.width, sourceImg.height);
|
||||
var tmp_context = tmp_canvas.getContext('2d');
|
||||
|
||||
var resultImageData = tmp_context.createImageData(sourceImg.width, sourceImg.height);
|
||||
var result = resultImageData.data;
|
||||
|
||||
if(op === 'or') {
|
||||
while(length > 0) {
|
||||
resultImageData.data[length] = 255;
|
||||
result[length-1] = source[length-1] | dest[length-1];
|
||||
result[length-2] = source[length-2] | dest[length-2];
|
||||
result[length-3] = source[length-3] | dest[length-3];
|
||||
length-=4;
|
||||
}
|
||||
} else if(op === 'and') {
|
||||
while(length > 0) {
|
||||
resultImageData.data[length] = 255;
|
||||
result[length-1] = source[length-1] & dest[length-1];
|
||||
result[length-2] = source[length-2] & dest[length-2];
|
||||
result[length-3] = source[length-3] & dest[length-3];
|
||||
length-=4;
|
||||
}
|
||||
} else if(op === 'xor') {
|
||||
while(length > 0) {
|
||||
resultImageData.data[length] = 255;
|
||||
result[length-1] = source[length-1] ^ dest[length-1];
|
||||
result[length-2] = source[length-2] ^ dest[length-2];
|
||||
result[length-3] = source[length-3] ^ dest[length-3];
|
||||
length-=4;
|
||||
}
|
||||
}
|
||||
tmp_context.putImageData(resultImageData, 0, 0);
|
||||
return tmp_canvas;
|
||||
}
|
||||
};
|
200
js/spice-web-client/application/spiceconnection.js
Normal file
200
js/spice-web-client/application/spiceconnection.js
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SpiceConnection = $.spcExtend(wdi.EventObject.prototype, {
|
||||
channels:null,
|
||||
connectionId: null,
|
||||
connectionInfo: null,
|
||||
runQ: null,
|
||||
token: null,
|
||||
connectionControl: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.channels = {};
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN] = c.mainChannel || new wdi.SpiceChannel();
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_DISPLAY] = c.displayChannel || new wdi.SpiceChannel();
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_INPUTS] = c.inputsChannel || new wdi.SpiceChannel();
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_CURSOR] = c.cursorChannel || new wdi.SpiceChannel();
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK] = c.playbackChannel || new wdi.SpiceChannel();
|
||||
this.runQ = c.runQ || new wdi.RunQueue();
|
||||
this.connectionControl = c.connectionControl || new wdi.ConnectionControl();
|
||||
this.setup();
|
||||
},
|
||||
|
||||
connect: function(connectionInfo) {
|
||||
this.connectionInfo = connectionInfo;
|
||||
if (connectionInfo.connectionControl) {
|
||||
this.connectionControl.connect(connectionInfo);
|
||||
}
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].connect(this.connectionInfo, wdi.SpiceVars.SPICE_CHANNEL_MAIN);
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
for (var i in this.channels) {
|
||||
this.channels[i].disconnect();
|
||||
this.channels[i] = null;
|
||||
delete(this.channels[i]);
|
||||
}
|
||||
this.connectionControl.disconnect();
|
||||
},
|
||||
|
||||
send: function(spcMessage) {
|
||||
var data = spcMessage.args.marshall();
|
||||
if(this.channels[spcMessage.channel]) {
|
||||
this.channels[spcMessage.channel].sendObject(
|
||||
data,
|
||||
spcMessage.messageType
|
||||
);
|
||||
} else {
|
||||
console.error("channel not available", spcMessage.channel);
|
||||
}
|
||||
},
|
||||
|
||||
//set events to all channels
|
||||
setup: function() {
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('connectionId', this.onConnectionId, this);
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('channelListAvailable', this.onChannelList, this);
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('mouseMode', this.onMouseMode, this);
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('initAgent', this.onInitAgent, this);
|
||||
this.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN].addListener('notify', this.onNotify, this);
|
||||
this.connectionControl.addListener('connectionLost', this.onDisconnect, this);
|
||||
|
||||
this._setConnectedListeners();
|
||||
|
||||
var f = null;
|
||||
if(wdi.exceptionHandling) {
|
||||
f = this.onChannelMessageExceptionHandled;
|
||||
} else {
|
||||
f = this.processChannelMessage;
|
||||
}
|
||||
|
||||
for(var i in this.channels) {
|
||||
if(this.channels.hasOwnProperty(i)) {
|
||||
this.channels[i].addListener('message', f, this);
|
||||
this.channels[i].addListener('status', this.onStatus, this);
|
||||
this.channels[i].addListener('error', this.onDisconnect, this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_setConnectedListeners: function() {
|
||||
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_MAIN);
|
||||
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_DISPLAY);
|
||||
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_INPUTS);
|
||||
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_CURSOR);
|
||||
this._setConnectedListener(wdi.SpiceVars.SPICE_CHANNEL_PLAYBACK);
|
||||
},
|
||||
|
||||
|
||||
_setConnectedListener: function(channel) {
|
||||
this.channels[channel].addListener('channelConnected', function () {
|
||||
this.fire('channelConnected', channel);
|
||||
}, this);
|
||||
},
|
||||
|
||||
onDisconnect: function(params) {
|
||||
this.fire("error", params);
|
||||
},
|
||||
|
||||
//events
|
||||
onConnectionId: function(params) {
|
||||
this.connectionId = params;
|
||||
},
|
||||
|
||||
onChannelList: function(params) {
|
||||
this.connectChannels(params);
|
||||
},
|
||||
|
||||
connectChannels: function(channels) {
|
||||
for(var i in this.channels) {
|
||||
i = parseInt(i, 10);
|
||||
if(i != wdi.SpiceVars.SPICE_CHANNEL_MAIN && channels.indexOf(i) != -1) {
|
||||
this.runQ.add(function(proxy, params) {
|
||||
this.channels[params].connect(this.connectionInfo, params, this.connectionId, proxy);
|
||||
}, this, false, i);
|
||||
}
|
||||
}
|
||||
this.runQ.process();
|
||||
},
|
||||
|
||||
onInitAgent: function(params) {
|
||||
var tokens = params;
|
||||
this.fire('initAgent', tokens);
|
||||
},
|
||||
|
||||
onMouseMode: function(params) {
|
||||
var mode = params;
|
||||
this.fire('mouseMode', mode);
|
||||
},
|
||||
|
||||
onNotify: function(params) {
|
||||
this.fire('notify');
|
||||
},
|
||||
|
||||
onStatus: function(params) {
|
||||
/*var status = params[1];
|
||||
var channel = params[2];
|
||||
if (status == wdi.CHANNEL_STATUS.idle) {
|
||||
var self = this;
|
||||
this.channels[channel].timer = setTimeout(function() {
|
||||
self.channels[channel].connect(self.host, self.port, channel, self.connectionId);
|
||||
}, 800);
|
||||
} else if (status == wdi.CHANNEL_STATUS.establishing) {
|
||||
clearTimeout(this.channels[channel].timer);
|
||||
}*/
|
||||
},
|
||||
|
||||
|
||||
onChannelMessageExceptionHandled: function(params) {
|
||||
try {
|
||||
return this.processChannelMessage(params);
|
||||
} catch(e) {
|
||||
wdi.Debug.error('SpiceConnection: Packet decodification error', e);
|
||||
}
|
||||
},
|
||||
|
||||
processChannelMessage: function(params) {
|
||||
var packet = wdi.PacketFactory.extract(params); //returns domain object
|
||||
|
||||
//return ViewQueue to the pool, object is already decoded
|
||||
wdi.GlobalPool.discard('ViewQueue', params.body);
|
||||
wdi.GlobalPool.discard('RawSpiceMessage', params);
|
||||
|
||||
if(packet) {
|
||||
this.fire('message', packet);
|
||||
} else {
|
||||
wdi.Debug.log('Unknown packet '+params.channel+' '+params.header.type);
|
||||
wdi.Debug.log(params);
|
||||
}
|
||||
}
|
||||
});
|
51
js/spice-web-client/application/stream.js
Normal file
51
js/spice-web-client/application/stream.js
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.Stream = {
|
||||
streams: {},
|
||||
|
||||
addStream: function(id, stream) {
|
||||
this.streams[id] = stream;
|
||||
},
|
||||
|
||||
deleteStream: function(id) {
|
||||
this.streams[id] = undefined;
|
||||
},
|
||||
|
||||
getStream: function(id) {
|
||||
return this.streams[id];
|
||||
},
|
||||
|
||||
clip: function(id, clip) {
|
||||
this.streams[id].clip = clip;
|
||||
}
|
||||
}
|
95
js/spice-web-client/application/virtualmouse.js
Normal file
95
js/spice-web-client/application/virtualmouse.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.VirtualMouse = {
|
||||
eventLayers: [],
|
||||
mouseData:null,
|
||||
visible: null,
|
||||
lastLayer: null,
|
||||
hotspot: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
lastMousePosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
},
|
||||
|
||||
setHotspot: function(x, y) {
|
||||
this.hotspot.x = x;
|
||||
this.hotspot.y = y;
|
||||
},
|
||||
|
||||
setEventLayer: function(ev, x, y, width, height, position) {
|
||||
this.eventLayers.push({
|
||||
layer: ev,
|
||||
left: x,
|
||||
top: y,
|
||||
right: x+width,
|
||||
bottom: y+height,
|
||||
position: position
|
||||
});
|
||||
},
|
||||
|
||||
removeEventLayer: function(ev) {
|
||||
var len = this.eventLayers.length;
|
||||
for(var i=0;i<len;i++) {
|
||||
if(this.eventLayers[i].layer.id === ev.id) {
|
||||
this.eventLayers[ev.id] = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getEventLayer: function(x, y) {
|
||||
var len = this.eventLayers.length;
|
||||
var layer = null;
|
||||
for(var i=0;i<len;i++) {
|
||||
layer = this.eventLayers[i];
|
||||
if(x >= layer.left && x <= layer.right && y >= layer.top && y <= layer.bottom) {
|
||||
return layer.layer;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setMouse: function(mouseData, x, y) {
|
||||
//if(!Modernizr.touch) {
|
||||
var layer = null;
|
||||
var len = this.eventLayers.length;
|
||||
for(var i=0;i<len;i++) {
|
||||
layer = this.eventLayers[i];
|
||||
layer.layer.style.cursor = 'url('+mouseData+') ' + x + ' ' + y + ', default';
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
117
js/spice-web-client/benchmark.html
Normal file
117
js/spice-web-client/benchmark.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script type="text/javascript" src="application/WorkerProcess.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery-2.0.3.js"></script>
|
||||
<script type="text/javascript" src="lib/base64.js"></script>
|
||||
<script>
|
||||
function start() {
|
||||
//load benchmark data
|
||||
// $.get('recorded/quic_2658778_1366x854', function(data) {
|
||||
$.get('recorded/lz_rgba_3616376_1920x901', function(data) {
|
||||
|
||||
var width = 1920;
|
||||
var height = 901;
|
||||
|
||||
var decoded = Base64.decode(data);
|
||||
var arr = new ArrayBuffer(decoded.length);
|
||||
var u8 = new Uint8Array(arr);
|
||||
u8.set(decoded);
|
||||
var result = dispatch(arr, false);
|
||||
var tmpCanvas = $("<canvas/>")[0];
|
||||
tmpCanvas.width = width;
|
||||
tmpCanvas.height = height;
|
||||
|
||||
var imgData = tmpCanvas.getContext('2d').createImageData(width, height);
|
||||
var arrResult = new Uint8Array(result);
|
||||
imgData.data.set(arrResult);
|
||||
|
||||
var info = document.createTextNode('width: '+width+ ' height: '+height);
|
||||
$('body').append(info);
|
||||
$('body').append($('<br/>'));
|
||||
tmpCanvas.getContext('2d').putImageData(imgData, 0, 0, 0, 0, width, height);
|
||||
$('body').append(tmpCanvas);
|
||||
$('body').append($('<br/>'));
|
||||
|
||||
//create benchmark button!
|
||||
var btn = $("<input/>").attr({
|
||||
'type': 'button',
|
||||
'value': 'start'
|
||||
});
|
||||
|
||||
btn.click(function() {
|
||||
var start = null;
|
||||
var end = null;
|
||||
var max = 0;
|
||||
var min = 9999999999999999;
|
||||
var used = null;
|
||||
var sum = 0;
|
||||
var loops = 250;
|
||||
for(var i=0;i<loops;i++) {
|
||||
start = Date.now();
|
||||
result = dispatch(arr, false);
|
||||
end = Date.now();
|
||||
used = end-start;
|
||||
sum = sum+(used);
|
||||
|
||||
if(used > max) {
|
||||
max = used;
|
||||
}
|
||||
|
||||
if(used < min) {
|
||||
min = used;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
console.log('min: ', min);
|
||||
console.log('max: ', max);
|
||||
console.log('mean: ', sum/loops);
|
||||
$('body').append($('<br/>'));
|
||||
$('body').append(document.createTextNode('min: '+min+ ' max: '+max+ ' mean: '+sum/loops));
|
||||
|
||||
});
|
||||
|
||||
$('body').append(btn);
|
||||
});
|
||||
}
|
||||
$(document).ready(start);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
19
js/spice-web-client/commit-stage.sh
Executable file
19
js/spice-web-client/commit-stage.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -u
|
||||
set -x
|
||||
|
||||
# it would be good to not run the build if:
|
||||
#
|
||||
# * there are no changes in the source files used in the concatenator.js file
|
||||
# * there haven't been any changes in this repo
|
||||
#
|
||||
# since the last build done by jenkins. To achieve this you might get the date
|
||||
# of the last jenkins commit in this repo, and do git log --since 'that date'
|
||||
# and check if there have been any commit or not.
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "Generating spiceproxy.js"
|
||||
|
||||
spiceproxy/concatenator.js spice-web-client
|
BIN
js/spice-web-client/eyeos128.png
Normal file
BIN
js/spice-web-client/eyeos128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
136
js/spice-web-client/index.html
Normal file
136
js/spice-web-client/index.html
Normal file
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>eyeOS Spice Web Client</title>
|
||||
<meta charset="utf-8">
|
||||
<!-- libs -->
|
||||
<script src="lib/modernizr.js"></script>
|
||||
<script src="lib/jquery-2.0.3.js"></script>
|
||||
<script src="lib/jquery-mousewheel.js"></script>
|
||||
<script src="lib/jgestures.min.js"></script>
|
||||
<script src="lib/pixastic.js"></script>
|
||||
<script src="lib/base64.js"></script>
|
||||
<script src="lib/biginteger.js"></script>
|
||||
<script src="lib/virtualjoystick.js"></script>
|
||||
<script src="lib/prettyprint.js"></script>
|
||||
<!-- ticketing -->
|
||||
<script src="lib/jsbn.js"></script>
|
||||
<script src="lib/jsbn2.js"></script>
|
||||
<script src="lib/prng4.js"></script>
|
||||
<script src="lib/rng.js"></script>
|
||||
<script src="lib/sha1.js"></script>
|
||||
<script src="lib/encrypt.js"></script>
|
||||
<!-- end libs -->
|
||||
<!-- core -->
|
||||
<script src="swcanvas/swcanvas.js"></script>
|
||||
<script src="lib/bowser.js"></script>
|
||||
<script src="lib/utils.js"></script>
|
||||
<script src="lib/flipper.js"></script>
|
||||
<script src="lib/CollisionDetector.js"></script>
|
||||
<script src="lib/GlobalPool.js"></script>
|
||||
<script src="lib/GenericObjectPool.js"></script>
|
||||
<script src="lib/AsyncConsumer.js"></script>
|
||||
<script src="lib/AsyncWorker.js"></script>
|
||||
<script src="lib/PacketWorkerIdentifier.js"></script>
|
||||
<script src="spiceobjects/spiceobjects.js"></script>
|
||||
<script src="spiceobjects/generated/protocol.js"></script>
|
||||
<script src="lib/graphicdebug.js"></script>
|
||||
<script src="lib/images/lz.js"></script>
|
||||
<script src="lib/images/bitmap.js"></script>
|
||||
<script src="lib/images/png.js"></script>
|
||||
<script src="lib/runqueue.js"></script>
|
||||
<script src="lib/graphic.js"></script>
|
||||
<script src="lib/queue.js"></script>
|
||||
<script src="lib/ImageUncompressor.js"></script>
|
||||
<script src="lib/SyncAsyncHandler.js"></script>
|
||||
<script src="lib/IntegrationBenchmark.js"></script>
|
||||
<script src="lib/stuckkeyshandler.js"></script>
|
||||
<script src="lib/timelapsedetector.js"></script>
|
||||
<script src="lib/displayRouter.js"></script>
|
||||
<script src="lib/rasterEngine.js"></script>
|
||||
<script src="lib/DataLogger.js"></script>
|
||||
<script src="network/socket.js"></script>
|
||||
<script src="network/clusternodechooser.js"></script>
|
||||
<script src="network/socketqueue.js"></script>
|
||||
<script src="network/packetcontroller.js"></script>
|
||||
<script src="network/packetextractor.js"></script>
|
||||
<script src="network/packetreassembler.js"></script>
|
||||
<script src="network/reassemblerfactory.js"></script>
|
||||
<script src="network/sizedefiner.js"></script>
|
||||
<script src="network/packetlinkfactory.js"></script>
|
||||
<script src="network/spicechannel.js"></script>
|
||||
<script src="network/busconnection.js"></script>
|
||||
<script src="network/websocketwrapper.js"></script>
|
||||
<script src="network/connectioncontrol.js"></script>
|
||||
<script src="application/agent.js"></script>
|
||||
<script src="application/spiceconnection.js"></script>
|
||||
<script src="application/clientgui.js"></script>
|
||||
<script src="application/packetprocess.js"></script>
|
||||
<script src="application/packetfilter.js"></script>
|
||||
<script src="application/packetfactory.js"></script>
|
||||
<script src="application/application.js"></script>
|
||||
<script src="application/virtualmouse.js"></script>
|
||||
<script src="application/imagecache.js"></script>
|
||||
<script src="application/rasteroperation.js"></script>
|
||||
<script src="application/stream.js"></script>
|
||||
<script src="application/inputmanager.js"></script>
|
||||
<script src="process/busprocess.js"></script>
|
||||
<script src="process/displayprocess.js"></script>
|
||||
<script src="process/displaypreprocess.js"></script>
|
||||
<script src="process/inputprocess.js"></script>
|
||||
<script src="process/cursorprocess.js"></script>
|
||||
<script src="process/playbackprocess.js"></script>
|
||||
<script src="process/mainprocess.js"></script>
|
||||
<script src="keymaps/keymapes.js"></script>
|
||||
<script src="keymaps/keymapit.js"></script>
|
||||
<script src="keymaps/keymapus.js"></script>
|
||||
<script src="keymaps/keymap.js"></script>
|
||||
<script src="application/WorkerProcess.js"></script>
|
||||
<script src="run.js"></script>
|
||||
<!-- end core -->
|
||||
<meta content="yes" name="apple-mobile-web-app-capable" />
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color:black;
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
129
js/spice-web-client/keymaps/keymap.js
Normal file
129
js/spice-web-client/keymaps/keymap.js
Normal file
@ -0,0 +1,129 @@
|
||||
wdi.keyShortcutsHandled = {
|
||||
CTRLV: 0
|
||||
}
|
||||
|
||||
wdi.Keymap = {
|
||||
keymap: {},
|
||||
ctrlKeymap: {},
|
||||
charmap: {},
|
||||
ctrlPressed: false,
|
||||
twoBytesScanCodes: [0x5B, 0xDB, /*0x38, 0xB8,*/ 0x5C, 0xDC, 0x1D, 0x9D, 0x5D, 0xDD, 0x52, 0xD2, 0x53, 0xD3, 0x4B, 0xCB, 0x47, 0xC9, 0x4F, 0xCF, 0x48, 0xC8, 0x50, 0xD0, 0x49, 0xC9, 0x51, 0xD1, 0x4D, 0xCD, 0x1C, 0x9C],
|
||||
|
||||
loadKeyMap: function(layout) {
|
||||
try {
|
||||
this.keymap = wdi['Keymap' + layout.toUpperCase()].getKeymap();
|
||||
this.ctrlKeymap = wdi['Keymap' + layout.toUpperCase()].getCtrlKeymap();
|
||||
this.reservedCtrlKeymap = wdi['Keymap' + layout.toUpperCase()].getReservedCtrlKeymap();
|
||||
this.charmap = wdi['Keymap' + layout.toUpperCase()].getCharmap();
|
||||
} catch(e) {
|
||||
this.keymap = wdi.KeymapES.getKeymap();
|
||||
this.ctrlKeymap = wdi.KeymapES.getCtrlKeymap();
|
||||
this.reservedCtrlKeymap = wdi.KeymapES.getReservedCtrlKeymap();
|
||||
this.charmap = wdi.KeymapES.getCharmap();
|
||||
}
|
||||
},
|
||||
|
||||
isInKeymap: function(keycode) {
|
||||
if (this.keymap[keycode] === undefined) return false;
|
||||
else return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the associated spice key code from the given browser keyboard event
|
||||
* @param e
|
||||
* @returns {*}
|
||||
*/
|
||||
getScanCodes: function(e) {
|
||||
if (e['hasScanCode']) {
|
||||
return e['scanCode'];
|
||||
} else if (this.handledByCtrlKeyCode(e['type'], e['keyCode'], e['generated'])) {// before doing anything else we check if the event about to be handled has to be intercepted
|
||||
return this.getScanCodeFromKeyCode(e['keyCode'], e['type'], this.ctrlKeymap, this.reservedCtrlKeymap);
|
||||
} else if (this.handledByCharmap(e['type'])) {
|
||||
return this.getScanCodesFromCharCode(e['charCode']);
|
||||
} else if (this.handledByNormalKeyCode(e['type'], e['keyCode'])) {
|
||||
return this.getScanCodeFromKeyCode(e['keyCode'], e['type'], this.keymap);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
getScanCodeFromKeyCode: function(keyCode, type, keymap, additionalKeymap) {
|
||||
this.controlPressed(keyCode, type);
|
||||
var key = null;
|
||||
if(keyCode in keymap) {
|
||||
key = keymap[keyCode];
|
||||
} else {
|
||||
key = additionalKeymap[keyCode];
|
||||
}
|
||||
if (key === undefined) return [];
|
||||
if (key < 0x100) {
|
||||
if (type == 'keydown') {
|
||||
return [this.makeKeymap(key)];
|
||||
} else if (type == 'keyup') {
|
||||
return [this.makeKeymap(key | 0x80)];
|
||||
}
|
||||
} else {
|
||||
if (type == 'keydown') {
|
||||
return [this.makeKeymap(0xe0 | ((key - 0x100) << 8))];
|
||||
} else if (type == 'keyup') {
|
||||
return [this.makeKeymap(0x80e0 | ((key - 0x100) << 8))];
|
||||
}
|
||||
}
|
||||
return key;
|
||||
},
|
||||
|
||||
controlPressed: function(keyCode, type) {
|
||||
if (keyCode === 17 || keyCode === 91) { // Ctrl or CMD key
|
||||
if (type === 'keydown') this.ctrlPressed = true;
|
||||
else if (type === 'keyup') this.ctrlPressed = false;
|
||||
}
|
||||
},
|
||||
|
||||
handledByCtrlKeyCode: function(type, keyCode, generated) {
|
||||
if (type === 'keydown' || type === 'keyup' || type === 'keypress') {
|
||||
if (this.ctrlPressed) {
|
||||
if (type === 'keypress') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ctrlKeymap[keyCode]) {
|
||||
return true; // is the second key in a keyboard shortcut (i.e. the x in Ctrl+x)
|
||||
}
|
||||
|
||||
//check if the event is a fake event generated from our gui or programatically
|
||||
if(generated && this.reservedCtrlKeymap[keyCode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
handledByNormalKeyCode: function(type, keyCode) {
|
||||
if (type === 'keydown' || type === 'keyup') {
|
||||
if (this.keymap[keyCode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
handledByCharmap: function(type) {
|
||||
if (type === 'inputmanager') return true;
|
||||
else return false;
|
||||
},
|
||||
|
||||
getScanCodesFromCharCode: function(charCode) {
|
||||
var scanCode = this.charmap[String.fromCharCode(charCode)];
|
||||
if (scanCode === undefined) scanCode = [];
|
||||
return scanCode;
|
||||
},
|
||||
|
||||
makeKeymap: function(scancode) {
|
||||
if ($.inArray(scancode, this.twoBytesScanCodes) != -1) {
|
||||
return [0xE0, scancode, 0, 0];
|
||||
} else {
|
||||
return [scancode, 0, 0];
|
||||
}
|
||||
}
|
||||
}
|
251
js/spice-web-client/keymaps/keymapes.js
Normal file
251
js/spice-web-client/keymaps/keymapes.js
Normal file
@ -0,0 +1,251 @@
|
||||
// These tables map the js keyboard keys to the spice equivalent
|
||||
wdi.KeymapES = function() {
|
||||
|
||||
// regular keys with associated chars. The columns means all the event flux to activate the key (i.e. [key up, key down])
|
||||
// all the js events associated to these keys should have a charKey associated
|
||||
var charmapES = {};
|
||||
charmapES['º'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
|
||||
charmapES['ª'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['\\'] = [[0xE0, 0x38, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['1'] = [[0x2, 0, 0, 0], [0x82, 0, 0, 0]];
|
||||
charmapES['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['|'] = [[0xE0, 0x38, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
|
||||
charmapES['"'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['@'] = [[0xE0, 0x38, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
|
||||
charmapES['·'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['#'] = [[0xE0, 0x38, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
|
||||
charmapES['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['~'] = [[0xE0, 0x38, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
|
||||
charmapES['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
|
||||
charmapES['&'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['¬'] = [[0xE0, 0x38, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
|
||||
charmapES['/'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
|
||||
charmapES['('] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
|
||||
charmapES[')'] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
|
||||
charmapES['='] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['\''] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
|
||||
charmapES['?'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['¡'] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
|
||||
charmapES['¿'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
|
||||
charmapES['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
|
||||
charmapES['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['€'] = [[0xE0, 0x38, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
|
||||
charmapES['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
|
||||
charmapES['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
|
||||
charmapES['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
|
||||
charmapES['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['`'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
|
||||
charmapES['à'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['À'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['è'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['È'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['ì'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['Ì'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['ò'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['Ò'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['ù'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['Ù'] = [[0xAA, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['â'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['Â'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['ê'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['Ê'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['î'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['Î'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['ô'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['Ô'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['û'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['Û'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['^'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
|
||||
charmapES['['] = [[0xE0, 0x38, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['+'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
|
||||
charmapES['*'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES[']'] = [[0xE0, 0x38, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
|
||||
charmapES['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
|
||||
charmapES['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
|
||||
charmapES['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
|
||||
charmapES['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
|
||||
charmapES['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
|
||||
charmapES['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
|
||||
charmapES['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
|
||||
charmapES['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['ñ'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
|
||||
charmapES['Ñ'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['á'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['Á'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['é'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['É'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['í'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['Í'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['ó'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['Ó'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['ú'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['Ú'] = [[0xAA, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['ä'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['Ä'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapES['ë'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['Ë'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapES['ï'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['Ï'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapES['ö'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['Ö'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapES['ü'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['Ü'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapES['{'] = [[0xE0, 0x38, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['ç'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
|
||||
charmapES['Ç'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['}'] = [[0xE0, 0x38, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapES['<'] = [[0x56, 0, 0, 0], [0xD6, 0, 0, 0]];
|
||||
charmapES['>'] = [[0x2A, 0, 0, 0], [0x56, 0, 0, 0], [0xD6, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
|
||||
charmapES['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
|
||||
charmapES['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
|
||||
charmapES['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
|
||||
charmapES['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
|
||||
charmapES['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
|
||||
charmapES['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
|
||||
charmapES['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
|
||||
charmapES[';'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
|
||||
charmapES[':'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES['-'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
|
||||
charmapES['_'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapES[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
|
||||
|
||||
// keyboard keys without character associated.
|
||||
// all the js events associated to these keys should have a keyChar associated
|
||||
var keymapES = [];
|
||||
|
||||
keymapES[27] = 0x1; // ESC
|
||||
keymapES[9] = 0x0F; // TAB
|
||||
//keymapES[20] = 0x3A; // BLOQ.MAY. => see the charmap, all the capital letters and shift chars send a shift in their sequence
|
||||
keymapES[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
|
||||
keymapES[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL
|
||||
keymapES[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
|
||||
//keymapES[32] = 0x39; // SPACE => see the charmap
|
||||
keymapES[8] = 0x0E; // BACKSPACE
|
||||
keymapES[13] = 0x1C; // ENTER
|
||||
//keymapES[225] = 0x38; // RIGHT ALT (ALT GR) => see the charmap, all the altgr chars send a altgr in their sequence
|
||||
keymapES[18] = 0x38; // LEFT ALT
|
||||
// keymapES[92] = 0x5C; // RIGHT GUI (WINDOWS)
|
||||
keymapES[38] = 0x48; // UP ARROW
|
||||
keymapES[37] = 0x4B; // LEFT ARROW
|
||||
keymapES[40] = 0x50; // DOWN ARROW
|
||||
keymapES[39] = 0x4D; // RIGHT ARROW
|
||||
keymapES[45] = 0x52; // INSERT
|
||||
keymapES[46] = 0x53; // DELETE
|
||||
keymapES[36] = 0x47; // HOME
|
||||
keymapES[35] = 0x4F; // FIN
|
||||
keymapES[33] = 0x49; // PAGE UP
|
||||
keymapES[34] = 0x51; // PAGE UP
|
||||
keymapES[144] = 0x45; // BLOQ.NUM.
|
||||
keymapES[145] = 0x46; // SCROLL LOCK
|
||||
keymapES[112] = 0x3B; // F1
|
||||
keymapES[113] = 0x3C; // F2
|
||||
keymapES[114] = 0x3D; // F3
|
||||
keymapES[115] = 0x3E; // F4
|
||||
keymapES[116] = 0x3F; // F5
|
||||
keymapES[117] = 0x40; // F6
|
||||
keymapES[118] = 0x41; // F7
|
||||
keymapES[119] = 0x42; // F8
|
||||
keymapES[120] = 0x43; // F9
|
||||
keymapES[121] = 0x44; // F10
|
||||
keymapES[122] = 0x57; // F11
|
||||
keymapES[123] = 0x58; // F12
|
||||
|
||||
// combination keys with ctrl
|
||||
var ctrlKeymapES = [];
|
||||
|
||||
ctrlKeymapES[65] = 0x1E; // a
|
||||
ctrlKeymapES[81] = 0x10; // q
|
||||
ctrlKeymapES[87] = 0x11; // w
|
||||
ctrlKeymapES[69] = 0x12; // e
|
||||
ctrlKeymapES[82] = 0x13; // r
|
||||
ctrlKeymapES[84] = 0x14; // t
|
||||
ctrlKeymapES[89] = 0x15; // y
|
||||
ctrlKeymapES[85] = 0x16; // u
|
||||
ctrlKeymapES[73] = 0x17; // i
|
||||
ctrlKeymapES[79] = 0x18; // o
|
||||
ctrlKeymapES[80] = 0x19; // p
|
||||
ctrlKeymapES[65] = 0x1E; // a
|
||||
ctrlKeymapES[83] = 0x1F; // s
|
||||
ctrlKeymapES[68] = 0x20; // d
|
||||
ctrlKeymapES[70] = 0x21; // f
|
||||
ctrlKeymapES[71] = 0x22; // g
|
||||
ctrlKeymapES[72] = 0x23; // h
|
||||
ctrlKeymapES[74] = 0x24; // j
|
||||
ctrlKeymapES[75] = 0x25; // k
|
||||
ctrlKeymapES[76] = 0x26; // l
|
||||
ctrlKeymapES[90] = 0x2C; // z
|
||||
ctrlKeymapES[88] = 0x2D; // x
|
||||
ctrlKeymapES[67] = 0x2E; // c
|
||||
//ctrlKeymapES[86] = 0x2F; // v to enable set disableClipboard = true in run.js
|
||||
ctrlKeymapES[66] = 0x30; // b
|
||||
ctrlKeymapES[78] = 0x31; // n
|
||||
ctrlKeymapES[77] = 0x32; // m
|
||||
|
||||
// reserved ctrl+? combinations we want to intercept from browser and inject manually to spice
|
||||
var reservedCtrlKeymap = [];
|
||||
reservedCtrlKeymap[86] = 0x2F;
|
||||
|
||||
return {
|
||||
getKeymap: function() {
|
||||
return keymapES;
|
||||
},
|
||||
|
||||
getCtrlKeymap: function() {
|
||||
return ctrlKeymapES;
|
||||
},
|
||||
|
||||
getReservedCtrlKeymap: function() {
|
||||
return reservedCtrlKeymap;
|
||||
},
|
||||
|
||||
getCharmap: function() {
|
||||
return charmapES;
|
||||
},
|
||||
|
||||
setCtrlKey: function (key, val) {
|
||||
ctrlKeymapES[key] = val;
|
||||
}
|
||||
};
|
||||
}( );
|
215
js/spice-web-client/keymaps/keymapit.js
Normal file
215
js/spice-web-client/keymaps/keymapit.js
Normal file
@ -0,0 +1,215 @@
|
||||
// These tables map the js keyboard keys to the spice equivalent
|
||||
wdi.KeymapIT = function() {
|
||||
|
||||
// regular keys with associated chars. The columns means all the event flux to activate the key (i.e. [key up, key down])
|
||||
// all the js events associated to these keys should have a charKey associated
|
||||
var charmapIT = {};
|
||||
charmapIT['\\'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
|
||||
charmapIT['|'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['1'] = [[0x2, 0, 0, 0], [0x82, 0, 0, 0]];
|
||||
charmapIT['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
|
||||
charmapIT['"'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
|
||||
charmapIT['£'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
|
||||
charmapIT['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
|
||||
charmapIT['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
|
||||
charmapIT['&'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
|
||||
charmapIT['/'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
|
||||
charmapIT['('] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
|
||||
charmapIT[')'] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
|
||||
charmapIT['='] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['\''] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
|
||||
charmapIT['?'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['`'] = [[0xE0, 0x38, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['ì'] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
|
||||
charmapIT['^'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['~'] = [[0xE0, 0x38, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
|
||||
charmapIT['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
|
||||
charmapIT['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapIT['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['€'] = [[0xE0, 0x38, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
|
||||
charmapIT['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
|
||||
charmapIT['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
|
||||
charmapIT['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapIT['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapIT['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapIT['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
|
||||
charmapIT['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['è'] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0]];
|
||||
charmapIT['é'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['['] = [[0xE0, 0x38, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['{'] = [[0xE0, 0x38, 0, 0], [0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['+'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
|
||||
charmapIT['*'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT[']'] = [[0xE0, 0x38, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['}'] = [[0xE0, 0x38, 0, 0], [0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapIT['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
|
||||
charmapIT['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
|
||||
charmapIT['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
|
||||
charmapIT['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
|
||||
charmapIT['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
|
||||
charmapIT['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
|
||||
charmapIT['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
|
||||
charmapIT['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
|
||||
charmapIT['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['ò'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
|
||||
charmapIT['ç'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['@'] = [[0xE0, 0x38, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['à'] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0]];
|
||||
charmapIT['°'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['#'] = [[0xE0, 0x38, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xE0, 0xB8, 0, 0]];
|
||||
charmapIT['ù'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
|
||||
charmapIT['§'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['<'] = [[0x56, 0, 0, 0], [0xD6, 0, 0, 0]];
|
||||
charmapIT['>'] = [[0x2A, 0, 0, 0], [0x56, 0, 0, 0], [0xD6, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
|
||||
charmapIT['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
|
||||
charmapIT['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
|
||||
charmapIT['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
|
||||
charmapIT['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
|
||||
charmapIT['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
|
||||
charmapIT['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
|
||||
charmapIT['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
|
||||
charmapIT[';'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
|
||||
charmapIT[':'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT['-'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
|
||||
charmapIT['_'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapIT[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
|
||||
|
||||
// keyboard keys without character associated.
|
||||
// all the js events associated to these keys should have a keyChar associated
|
||||
var keymapIT = [];
|
||||
|
||||
keymapIT[27] = 0x1; // ESC
|
||||
keymapIT[9] = 0x0F; // TAB
|
||||
//keymapIT[20] = 0x3A; // CAPS LOCK => see the charmap, all the capital letters and shift chars send a shift in their sequence
|
||||
keymapIT[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
|
||||
keymapIT[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL (why? 0x5B)
|
||||
keymapIT[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
|
||||
//keymapIT[32] = 0x39; // SPACE => see the charmap
|
||||
keymapIT[8] = 0x0E; // BACKSPACE
|
||||
keymapIT[12] = 0x4C; // KP_BEGIN (showkey -s: 0x4C, 0xCC)
|
||||
keymapIT[13] = 0x1C; // ENTER
|
||||
//keymapIT[225] = 0x38; // RIGHT ALT (ALT GR) => see the charmap, all the altgr chars send a altgr in their sequence
|
||||
keymapIT[18] = 0x38; // LEFT ALT
|
||||
//keymapIT[19] = 0x??; // PAUSE (showkey -s: 0xE1 0x1D 0x45, 0xE1 0x9D 0xC5)
|
||||
//keymapIT[92] = 0x5C; // RIGHT GUI (WINDOWS) (I get 91 for the right too)
|
||||
keymapIT[93] = 0x5D; // MENU
|
||||
keymapIT[38] = 0x48; // UP ARROW
|
||||
keymapIT[37] = 0x4B; // LEFT ARROW
|
||||
keymapIT[40] = 0x50; // DOWN ARROW
|
||||
keymapIT[39] = 0x4D; // RIGHT ARROW
|
||||
//keymapIT[42] = 0x??; // PRINT (showkey -s: 0xE0 0x2A 0xE0 0x37, 0xE0 0xAA 0xE0 0xB7)
|
||||
keymapIT[45] = 0x52; // INSERT
|
||||
keymapIT[46] = 0x53; // DELETE
|
||||
keymapIT[36] = 0x47; // HOME
|
||||
keymapIT[35] = 0x4F; // END
|
||||
keymapIT[33] = 0x49; // PAGE UP
|
||||
keymapIT[34] = 0x51; // PAGE DOWN
|
||||
keymapIT[144] = 0x45; // NUM LOCK
|
||||
keymapIT[145] = 0x46; // SCROLL LOCK
|
||||
keymapIT[112] = 0x3B; // F1
|
||||
keymapIT[113] = 0x3C; // F2
|
||||
keymapIT[114] = 0x3D; // F3
|
||||
keymapIT[115] = 0x3E; // F4
|
||||
keymapIT[116] = 0x3F; // F5
|
||||
keymapIT[117] = 0x40; // F6
|
||||
keymapIT[118] = 0x41; // F7
|
||||
keymapIT[119] = 0x42; // F8
|
||||
keymapIT[120] = 0x43; // F9
|
||||
keymapIT[121] = 0x44; // F10
|
||||
keymapIT[122] = 0x57; // F11
|
||||
keymapIT[123] = 0x58; // F12
|
||||
|
||||
// combination keys with ctrl
|
||||
var ctrlKeymapIT = [];
|
||||
|
||||
ctrlKeymapIT[65] = 0x1E; // a
|
||||
ctrlKeymapIT[81] = 0x10; // q
|
||||
ctrlKeymapIT[87] = 0x11; // w
|
||||
ctrlKeymapIT[69] = 0x12; // e
|
||||
ctrlKeymapIT[82] = 0x13; // r
|
||||
ctrlKeymapIT[84] = 0x14; // t
|
||||
ctrlKeymapIT[89] = 0x15; // y
|
||||
ctrlKeymapIT[85] = 0x16; // u
|
||||
ctrlKeymapIT[73] = 0x17; // i
|
||||
ctrlKeymapIT[79] = 0x18; // o
|
||||
ctrlKeymapIT[80] = 0x19; // p
|
||||
ctrlKeymapIT[65] = 0x1E; // a
|
||||
ctrlKeymapIT[83] = 0x1F; // s
|
||||
ctrlKeymapIT[68] = 0x20; // d
|
||||
ctrlKeymapIT[70] = 0x21; // f
|
||||
ctrlKeymapIT[71] = 0x22; // g
|
||||
ctrlKeymapIT[72] = 0x23; // h
|
||||
ctrlKeymapIT[74] = 0x24; // j
|
||||
ctrlKeymapIT[75] = 0x25; // k
|
||||
ctrlKeymapIT[76] = 0x26; // l
|
||||
ctrlKeymapIT[90] = 0x2C; // z
|
||||
ctrlKeymapIT[88] = 0x2D; // x
|
||||
ctrlKeymapIT[67] = 0x2E; // c
|
||||
//ctrlKeymapIT[86] = 0x2F; // v to enable set disableClipboard = true in run.js
|
||||
ctrlKeymapIT[66] = 0x30; // b
|
||||
ctrlKeymapIT[78] = 0x31; // n
|
||||
ctrlKeymapIT[77] = 0x32; // m
|
||||
|
||||
// reserved ctrl+? combinations we want to intercept from browser and inject manually to spice
|
||||
var reservedCtrlKeymap = [];
|
||||
reservedCtrlKeymap[86] = 0x2F;
|
||||
|
||||
return {
|
||||
getKeymap: function() {
|
||||
return keymapIT;
|
||||
},
|
||||
|
||||
getCtrlKeymap: function() {
|
||||
return ctrlKeymapIT;
|
||||
},
|
||||
|
||||
getReservedCtrlKeymap: function() {
|
||||
return reservedCtrlKeymap;
|
||||
},
|
||||
|
||||
getCharmap: function() {
|
||||
return charmapIT;
|
||||
},
|
||||
|
||||
setCtrlKey: function (key, val) {
|
||||
ctrlKeymapIT[key] = val;
|
||||
}
|
||||
};
|
||||
}( );
|
193
js/spice-web-client/keymaps/keymapus.js
Normal file
193
js/spice-web-client/keymaps/keymapus.js
Normal file
@ -0,0 +1,193 @@
|
||||
wdi.KeymapUS = function() {
|
||||
|
||||
var charmapUS = [];
|
||||
charmapUS['`'] = [[0x29, 0, 0, 0], [0xA9, 0, 0, 0]];
|
||||
charmapUS['~'] = [[0x2A, 0, 0, 0], [0x29, 0, 0, 0], [0xA9, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['1'] = [[0x2, 0, 0, 0],[0x82, 0, 0, 0]];
|
||||
charmapUS['!'] = [[0x2A, 0, 0, 0], [0x2, 0, 0, 0], [0x82, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['2'] = [[0x3, 0, 0, 0], [0x83, 0, 0, 0]];
|
||||
charmapUS['@'] = [[0x2A, 0, 0, 0], [0x3, 0, 0, 0], [0x83, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['3'] = [[0x4, 0, 0, 0], [0x84, 0, 0, 0]];
|
||||
charmapUS['#'] = [[0x2A, 0, 0, 0], [0x4, 0, 0, 0], [0x84, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['4'] = [[0x5, 0, 0, 0], [0x85, 0, 0, 0]];
|
||||
charmapUS['$'] = [[0x2A, 0, 0, 0], [0x5, 0, 0, 0], [0x85, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['5'] = [[0x6, 0, 0, 0], [0x86, 0, 0, 0]];
|
||||
charmapUS['%'] = [[0x2A, 0, 0, 0], [0x6, 0, 0, 0], [0x86, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['6'] = [[0x7, 0, 0, 0], [0x87, 0, 0, 0]];
|
||||
charmapUS['^'] = [[0x2A, 0, 0, 0], [0x7, 0, 0, 0], [0x87, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['7'] = [[0x8, 0, 0, 0], [0x88, 0, 0, 0]];
|
||||
charmapUS['&'] = [[0x2A, 0, 0, 0], [0x8, 0, 0, 0], [0x88, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['8'] = [[0x9, 0, 0, 0], [0x89, 0, 0, 0]];
|
||||
charmapUS['*'] = [[0x2A, 0, 0, 0], [0x9, 0, 0, 0], [0x89, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['9'] = [[0x0A, 0, 0, 0], [0x8A, 0, 0, 0]];
|
||||
charmapUS['('] = [[0x2A, 0, 0, 0], [0x0A, 0, 0, 0], [0x8A, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['0'] = [[0x0B, 0, 0, 0], [0x8B, 0, 0, 0]];
|
||||
charmapUS[')'] = [[0x2A, 0, 0, 0], [0x0B, 0, 0, 0], [0x8B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['-'] = [[0x0C, 0, 0, 0], [0x8C, 0, 0, 0]];
|
||||
charmapUS['_'] = [[0x2A, 0, 0, 0], [0x0C, 0, 0, 0], [0x8C, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['='] = [[0x0D, 0, 0, 0], [0x8D, 0, 0, 0]];
|
||||
charmapUS['+'] = [[0x2A, 0, 0, 0], [0x0D, 0, 0, 0], [0x8D, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['q'] = [[0x10, 0, 0, 0], [0x90, 0, 0, 0]];
|
||||
charmapUS['Q'] = [[0x2A, 0, 0, 0], [0x10, 0, 0, 0], [0x90, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['w'] = [[0x11, 0, 0, 0], [0x91, 0, 0, 0]];
|
||||
charmapUS['W'] = [[0x2A, 0, 0, 0], [0x11, 0, 0, 0], [0x91, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['e'] = [[0x12, 0, 0, 0], [0x92, 0, 0, 0]];
|
||||
charmapUS['E'] = [[0x2A, 0, 0, 0], [0x12, 0, 0, 0], [0x92, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['r'] = [[0x13, 0, 0, 0], [0x93, 0, 0, 0]];
|
||||
charmapUS['R'] = [[0x2A, 0, 0, 0], [0x13, 0, 0, 0], [0x93, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['t'] = [[0x14, 0, 0, 0], [0x94, 0, 0, 0]];
|
||||
charmapUS['T'] = [[0x2A, 0, 0, 0], [0x14, 0, 0, 0], [0x94, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['y'] = [[0x15, 0, 0, 0], [0x95, 0, 0, 0]];
|
||||
charmapUS['Y'] = [[0x2A, 0, 0, 0], [0x15, 0, 0, 0], [0x95, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['u'] = [[0x16, 0, 0, 0], [0x96, 0, 0, 0]];
|
||||
charmapUS['U'] = [[0x2A, 0, 0, 0], [0x16, 0, 0, 0], [0x96, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['i'] = [[0x17, 0, 0, 0], [0x97, 0, 0, 0]];
|
||||
charmapUS['I'] = [[0x2A, 0, 0, 0], [0x17, 0, 0, 0], [0x97, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['o'] = [[0x18, 0, 0, 0], [0x98, 0, 0, 0]];
|
||||
charmapUS['O'] = [[0x2A, 0, 0, 0], [0x18, 0, 0, 0], [0x98, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['p'] = [[0x19, 0, 0, 0], [0x99, 0, 0, 0]];
|
||||
charmapUS['P'] = [[0x2A, 0, 0, 0], [0x19, 0, 0, 0], [0x99, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['['] = [[0x1A, 0, 0, 0], [0x9A, 0, 0, 0]];
|
||||
charmapUS['{'] = [[0x2A, 0, 0, 0], [0x1A, 0, 0, 0], [0x9A, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS[']'] = [[0x1B, 0, 0, 0], [0x9B, 0, 0, 0]];
|
||||
charmapUS['}'] = [[0x2A, 0, 0, 0], [0x1B, 0, 0, 0], [0x9B, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['\\'] = [[0x2B, 0, 0, 0], [0xAB, 0, 0, 0]];
|
||||
charmapUS['|'] = [[0x2A, 0, 0, 0], [0x2B, 0, 0, 0], [0xAB, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['a'] = [[0x1E, 0, 0, 0], [0x9E, 0, 0, 0]];
|
||||
charmapUS['A'] = [[0x2A, 0, 0, 0], [0x1E, 0, 0, 0], [0x9E, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['s'] = [[0x1F, 0, 0, 0], [0x9F, 0, 0, 0]];
|
||||
charmapUS['S'] = [[0x2A, 0, 0, 0], [0x1F, 0, 0, 0], [0x9F, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['d'] = [[0x20, 0, 0, 0], [0xA0, 0, 0, 0]];
|
||||
charmapUS['D'] = [[0x2A, 0, 0, 0], [0x20, 0, 0, 0], [0xA0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['f'] = [[0x21, 0, 0, 0], [0xA1, 0, 0, 0]];
|
||||
charmapUS['F'] = [[0x2A, 0, 0, 0], [0x21, 0, 0, 0], [0xA1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['g'] = [[0x22, 0, 0, 0], [0xA2, 0, 0, 0]];
|
||||
charmapUS['G'] = [[0x2A, 0, 0, 0], [0x22, 0, 0, 0], [0xA2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['h'] = [[0x23, 0, 0, 0], [0xA3, 0, 0, 0]];
|
||||
charmapUS['H'] = [[0x2A, 0, 0, 0], [0x23, 0, 0, 0], [0xA3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['j'] = [[0x24, 0, 0, 0], [0xA4, 0, 0, 0]];
|
||||
charmapUS['J'] = [[0x2A, 0, 0, 0], [0x24, 0, 0, 0], [0xA4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['k'] = [[0x25, 0, 0, 0], [0xA5, 0, 0, 0]];
|
||||
charmapUS['K'] = [[0x2A, 0, 0, 0], [0x25, 0, 0, 0], [0xA5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['l'] = [[0x26, 0, 0, 0], [0xA6, 0, 0, 0]];
|
||||
charmapUS['L'] = [[0x2A, 0, 0, 0], [0x26, 0, 0, 0], [0xA6, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS[';'] = [[0x27, 0, 0, 0], [0xA7, 0, 0, 0]];
|
||||
charmapUS[':'] = [[0x2A, 0, 0, 0], [0x27, 0, 0, 0], [0xA7, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['\''] = [[0x28, 0, 0, 0], [0xA8, 0, 0, 0]];
|
||||
charmapUS['"'] = [[0x2A, 0, 0, 0], [0x28, 0, 0, 0], [0xA8, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['z'] = [[0x2C, 0, 0, 0], [0xAC, 0, 0, 0]];
|
||||
charmapUS['Z'] = [[0x2A, 0, 0, 0], [0x2C, 0, 0, 0], [0xAC, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['x'] = [[0x2D, 0, 0, 0], [0xAD, 0, 0, 0]];
|
||||
charmapUS['X'] = [[0x2A, 0, 0, 0], [0x2D, 0, 0, 0], [0xAD, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['c'] = [[0x2E, 0, 0, 0], [0xAE, 0, 0, 0]];
|
||||
charmapUS['C'] = [[0x2A, 0, 0, 0], [0x2E, 0, 0, 0], [0xAE, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['v'] = [[0x2F, 0, 0, 0], [0xAF, 0, 0, 0]];
|
||||
charmapUS['V'] = [[0x2A, 0, 0, 0], [0x2F, 0, 0, 0], [0xAF, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['b'] = [[0x30, 0, 0, 0], [0xB0, 0, 0, 0]];
|
||||
charmapUS['B'] = [[0x2A, 0, 0, 0], [0x30, 0, 0, 0], [0xB0, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['n'] = [[0x31, 0, 0, 0], [0xB1, 0, 0, 0]];
|
||||
charmapUS['N'] = [[0x2A, 0, 0, 0], [0x31, 0, 0, 0], [0xB1, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['m'] = [[0x32, 0, 0, 0], [0xB2, 0, 0, 0]];
|
||||
charmapUS['M'] = [[0x2A, 0, 0, 0], [0x32, 0, 0, 0], [0xB2, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS[','] = [[0x33, 0, 0, 0], [0xB3, 0, 0, 0]];
|
||||
charmapUS['<'] = [[0x2A, 0, 0, 0], [0x33, 0, 0, 0], [0xB3, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['.'] = [[0x34, 0, 0, 0], [0xB4, 0, 0, 0]];
|
||||
charmapUS['>'] = [[0x2A, 0, 0, 0], [0x34, 0, 0, 0], [0xB4, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS['/'] = [[0x35, 0, 0, 0], [0xB5, 0, 0, 0]];
|
||||
charmapUS['?'] = [[0x2A, 0, 0, 0], [0x35, 0, 0, 0], [0xB5, 0, 0, 0], [0xAA, 0, 0, 0]];
|
||||
charmapUS[' '] = [[0x39, 0, 0, 0], [0xb9, 0, 0, 0]];
|
||||
|
||||
var keymapUS = [];
|
||||
|
||||
keymapUS[27] = 0x1; // ESC
|
||||
keymapUS[9] = 0x0F; // TAB
|
||||
//keymapUS[20] = 0x3A; // // BLOQ.MAY. => see the charmap, all the capital letters and shift chars send a shift in their sequence
|
||||
keymapUS[16] = 0x2A; // LEFT SHIFT and RIGHT SHIFT
|
||||
keymapUS[91] = 0x1D; // LEFT GUI (META, COMMAND) BINDED TO CONTROL
|
||||
keymapUS[17] = 0x1D; // LEFT CONTROL and RIGHT CONTROL
|
||||
keymapUS[32] = 0x39; // SPACE
|
||||
keymapUS[8] = 0x0E; // BACKSPACE
|
||||
keymapUS[13] = 0x1C; // ENTER
|
||||
//keymapUS[0] = 0x38; // RIGHT ALT (ALT GR)
|
||||
//keymapUS[92] = 0x5C; // RIGHT GUI (WINDOWS)
|
||||
keymapUS[38] = 0x48; // UP ARROW
|
||||
keymapUS[37] = 0x4B; // LEFT ARROW
|
||||
keymapUS[40] = 0x50; // DOWN ARROW
|
||||
keymapUS[39] = 0x4D; // RIGHT ARROW
|
||||
keymapUS[45] = 0x52; // INSERT
|
||||
keymapUS[46] = 0x53; // DELETE
|
||||
keymapUS[36] = 0x47; // HOME
|
||||
keymapUS[35] = 0x4F; // FIN
|
||||
keymapUS[33] = 0x49; // PAGE UP
|
||||
keymapUS[34] = 0x51; // PAGE UP
|
||||
keymapUS[144] = 0x45; // BLOQ.NUM.
|
||||
keymapUS[145] = 0x46; // SCROLL LOCK
|
||||
keymapUS[112] = 0x3B; // F1
|
||||
keymapUS[113] = 0x3C; // F2
|
||||
keymapUS[114] = 0x3D; // F3
|
||||
keymapUS[115] = 0x3E; // F4
|
||||
keymapUS[116] = 0x3F; // F5
|
||||
keymapUS[117] = 0x40; // F6
|
||||
keymapUS[118] = 0x41; // F7
|
||||
keymapUS[119] = 0x42; // F8
|
||||
keymapUS[120] = 0x43; // F9
|
||||
keymapUS[121] = 0x44; // F10
|
||||
keymapUS[122] = 0x57; // F11
|
||||
keymapUS[123] = 0x58; // F12
|
||||
|
||||
var ctrlkeymapUS = [];
|
||||
|
||||
ctrlkeymapUS[65] = 0x1E; // a
|
||||
ctrlkeymapUS[81] = 0x10; // q
|
||||
ctrlkeymapUS[87] = 0x11; // w
|
||||
ctrlkeymapUS[69] = 0x12; // e
|
||||
ctrlkeymapUS[82] = 0x13; // r
|
||||
ctrlkeymapUS[84] = 0x14; // t
|
||||
ctrlkeymapUS[89] = 0x15; // y
|
||||
ctrlkeymapUS[85] = 0x16; // u
|
||||
ctrlkeymapUS[73] = 0x17; // i
|
||||
ctrlkeymapUS[79] = 0x18; // o
|
||||
ctrlkeymapUS[80] = 0x19; // p
|
||||
ctrlkeymapUS[65] = 0x1E; // a
|
||||
ctrlkeymapUS[83] = 0x1F; // s
|
||||
ctrlkeymapUS[68] = 0x20; // d
|
||||
ctrlkeymapUS[70] = 0x21; // f
|
||||
ctrlkeymapUS[71] = 0x22; // g
|
||||
ctrlkeymapUS[72] = 0x23; // h
|
||||
ctrlkeymapUS[74] = 0x24; // j
|
||||
ctrlkeymapUS[75] = 0x25; // k
|
||||
ctrlkeymapUS[76] = 0x26; // l
|
||||
ctrlkeymapUS[90] = 0x2C; // z
|
||||
ctrlkeymapUS[88] = 0x2D; // x
|
||||
ctrlkeymapUS[67] = 0x2E; // c
|
||||
//ctrlkeymapUS[86] = 0x2F; // v to enable set disableClipboard = true in run.js
|
||||
ctrlkeymapUS[66] = 0x30; // b
|
||||
ctrlkeymapUS[78] = 0x31; // n
|
||||
ctrlkeymapUS[77] = 0x32; // m
|
||||
|
||||
var reservedCtrlKeymap = [];
|
||||
reservedCtrlKeymap[86] = 0x2F; //v
|
||||
|
||||
|
||||
return {
|
||||
getKeymap: function() {
|
||||
return keymapUS;
|
||||
},
|
||||
|
||||
getCtrlKeymap: function() {
|
||||
return ctrlkeymapUS;
|
||||
},
|
||||
|
||||
getReservedCtrlKeymap: function() {
|
||||
return reservedCtrlKeymap;
|
||||
},
|
||||
|
||||
getCharmap: function() {
|
||||
return charmapUS;
|
||||
},
|
||||
|
||||
setCtrlKey: function (key, val) {
|
||||
ctrlkeymapUS[key] = val;
|
||||
}
|
||||
};
|
||||
}( );
|
206
js/spice-web-client/lib/AsyncConsumer.js
Normal file
206
js/spice-web-client/lib/AsyncConsumer.js
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.AsyncConsumer = $.spcExtend(wdi.EventObject.prototype, {
|
||||
worker: null,
|
||||
task: null,
|
||||
packetWorkerIdentifier: null,
|
||||
imageProperties: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.worker = c.AsyncWorker || new wdi.AsyncWorker({script:'application/WorkerProcess.js'});
|
||||
this.packetWorkerIdentifier = c.packetWorkerIdentifier || new wdi.PacketWorkerIdentifier();
|
||||
},
|
||||
|
||||
consume: function(task) {
|
||||
this.task = task; //store current task
|
||||
var message = task.message;
|
||||
var imageProperties;
|
||||
|
||||
//check if the packet is a type of packet that should be intercepted
|
||||
//this doesn't mean it contains a compressed image, it means that it COULD
|
||||
var intercept = this.packetWorkerIdentifier.shouldUseWorker(message);
|
||||
|
||||
if(intercept == wdi.PacketWorkerIdentifier.processingType.DECOMPRESS) {
|
||||
//get image properties to check if there is really a compressed image
|
||||
imageProperties = this.packetWorkerIdentifier.getImageProperties(message);
|
||||
this.imageProperties = imageProperties;
|
||||
//compressed images are quic and lz
|
||||
if(imageProperties && (imageProperties.descriptor.type != wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB &&
|
||||
imageProperties.descriptor.type != wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC)) {
|
||||
|
||||
intercept = 0;
|
||||
} else if(!imageProperties) {
|
||||
intercept = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//the packet is not going to be intercepted by the worker thread.
|
||||
//mark as procssed.
|
||||
if(intercept === 0) {
|
||||
this.taskDone();
|
||||
return;
|
||||
}
|
||||
|
||||
var data;
|
||||
var descriptor;
|
||||
var opaque;
|
||||
var brush;
|
||||
var ret;
|
||||
var arr;
|
||||
var u8;
|
||||
|
||||
if(intercept == wdi.PacketWorkerIdentifier.processingType.DECOMPRESS) {
|
||||
data = imageProperties.data;
|
||||
descriptor = imageProperties.descriptor;
|
||||
opaque = imageProperties.opaque;
|
||||
brush = imageProperties.brush;
|
||||
|
||||
if(descriptor.type === wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB) {
|
||||
var header = null;
|
||||
|
||||
if(!brush) { //brushes are still js arrays
|
||||
var headerData = data.subarray(0,32).toJSArray();
|
||||
data = data.subarray(32); //skip the header
|
||||
header = wdi.LZSS.demarshall_rgb(headerData);
|
||||
} else {
|
||||
header = wdi.LZSS.demarshall_rgb(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
arr = new ArrayBuffer(data.length+16);
|
||||
u8 = new Uint8Array(arr);
|
||||
|
||||
u8[0] = 1; //LZ_RGB
|
||||
u8[1] = opaque;
|
||||
u8[2] = header.type;
|
||||
u8[3] = header.top_down; //RESERVED
|
||||
|
||||
var number = header.width * header.height * 4;
|
||||
|
||||
for (var i = 0;i < 4;i++) {//iterations because of javascript number size
|
||||
u8[4+i] = number & (255);//Get only the last byte
|
||||
number = number >> 8;//Remove the last byte
|
||||
}
|
||||
var view = new DataView(arr);
|
||||
view.setUint32(8, header.width);
|
||||
view.setUint32(12, header.height);
|
||||
u8.set(data, 16);
|
||||
|
||||
//intercept
|
||||
//var encoded = encodeURIComponent(Base64.encode(u8.toJSArray()));
|
||||
//$.post('record.php','data='+encoded+'&name=lz_rgba_'+encoded.length+'_'+descriptor.width+'x'+descriptor.height);
|
||||
|
||||
this.worker.run(arr, this._workerCompleted, {type: 'lz',top_down: header.top_down, opaque: opaque}, this);
|
||||
} else if(descriptor.type === wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC) {
|
||||
var adata = new ArrayBuffer(data.length+4);
|
||||
var view = new Uint8Array(adata);
|
||||
view.set(data, 4);
|
||||
view[1] = opaque?1:0;
|
||||
view[0] = 0; //quic
|
||||
|
||||
//intercept
|
||||
/*
|
||||
var jsarray = new Uint8Array(adata);
|
||||
var encoded = encodeURIComponent(Base64.encode(jsarray.toJSArray()));
|
||||
var dateat = Date.now() /1000;
|
||||
$.post('record.php','data='+encoded+'&name=quic_'+encoded.length+'_'+descriptor.width+'x'+descriptor.height);
|
||||
*/
|
||||
|
||||
this.worker.run(adata, this._workerCompleted, {type: 'quic'}, this);
|
||||
}
|
||||
} else if(intercept == wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO) {
|
||||
data = this.packetWorkerIdentifier.getVideoData(message);
|
||||
arr = new ArrayBuffer(data.length+4);
|
||||
u8 = new Uint8Array(arr);
|
||||
|
||||
u8[0] = 2; //2 means bytestouri
|
||||
u8[1] = 0;
|
||||
u8[2] = 0;
|
||||
u8[3] = 0; //reserved
|
||||
|
||||
u8.set(data, 4);
|
||||
this.worker.run(arr, function(buf, params) {
|
||||
message.args.data = buf;
|
||||
this.taskDone();
|
||||
}, null, this);
|
||||
}
|
||||
},
|
||||
|
||||
//executed from webworker when processing is finished
|
||||
_workerCompleted: function(buf, options) {
|
||||
if(!buf) {
|
||||
this.taskDone();
|
||||
return;
|
||||
}
|
||||
var descriptor = this.imageProperties.descriptor;
|
||||
var u8 = new Uint8ClampedArray(buf);
|
||||
var source_img = new ImageData(u8, descriptor.width, descriptor.height);
|
||||
|
||||
//it is strange, but we can't use pooling on the getimagefromdata
|
||||
//the second argument (optional) tell getimagefromdata to avoid pooling
|
||||
var myImage = source_img;
|
||||
|
||||
|
||||
if(options.type === 'lz') {
|
||||
var top_down = options.top_down;
|
||||
var opaque = options.opaque;
|
||||
if(!top_down && !opaque) {
|
||||
myImage = wdi.graphics.getImageFromData(source_img, true);
|
||||
myImage = wdi.RasterOperation.flip(myImage);
|
||||
}
|
||||
}
|
||||
|
||||
descriptor.originalType = descriptor.type;
|
||||
descriptor.type = wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS;
|
||||
|
||||
//replace data
|
||||
if(this.task.message.messageType === wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL) {
|
||||
this.task.message.args.brush.pattern.imageData = myImage;
|
||||
this.task.message.args.brush.pattern.image.type = wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS;
|
||||
} else {
|
||||
this.task.message.args.image.data = myImage;
|
||||
}
|
||||
this.taskDone();
|
||||
},
|
||||
|
||||
taskDone: function() {
|
||||
this.task.state = 1;
|
||||
this.fire('done', this);
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.worker.dispose();
|
||||
}
|
||||
});
|
63
js/spice-web-client/lib/AsyncWorker.js
Normal file
63
js/spice-web-client/lib/AsyncWorker.js
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.AsyncWorker = $.spcExtend(wdi.EventObject.prototype, {
|
||||
worker: null,
|
||||
fn: null,
|
||||
scope: null,
|
||||
params: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.worker = new Worker(c.script);
|
||||
var self = this;
|
||||
this.worker.addEventListener("message", function (oEvent) {
|
||||
self.fn.call(self.scope, oEvent.data, self.params);
|
||||
});
|
||||
},
|
||||
|
||||
run: function(data, fn, params, scope) {
|
||||
this.fn = fn;
|
||||
this.scope = scope;
|
||||
this.params = params;
|
||||
|
||||
if (wdi.postMessageW3CCompilant) {
|
||||
this.worker.postMessage(data, [data]);
|
||||
} else {
|
||||
this.worker.postMessage(data);
|
||||
}
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.worker.terminate();
|
||||
}
|
||||
});
|
40
js/spice-web-client/lib/CollisionDetector.js
Normal file
40
js/spice-web-client/lib/CollisionDetector.js
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.CollisionDetector = {
|
||||
thereIsBoxCollision: function(baseBox, queueBox) {
|
||||
if(baseBox.bottom < queueBox.top) return false;
|
||||
if(baseBox.top > queueBox.bottom) return false;
|
||||
if(baseBox.right < queueBox.left) return false;
|
||||
return baseBox.left < queueBox.right;
|
||||
}
|
||||
};
|
208
js/spice-web-client/lib/DataLogger.js
Normal file
208
js/spice-web-client/lib/DataLogger.js
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.DataLogger = {
|
||||
testStartTime: 0,
|
||||
testStopTime: 0,
|
||||
networkStart:0,
|
||||
networkTotalTime: 0,
|
||||
data: {},
|
||||
routeList: {},
|
||||
imageTypes: {},
|
||||
startTimes: [],
|
||||
init: function() {
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE] = 'drawCanvas';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY] = 'removeCanvas';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY] = 'drawImage';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL] = 'drawFill';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND] = 'drawAlphaBlend';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS] = 'drawWhiteness';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS] = 'drawBlackness';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT] = 'drawTransparent';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS] = 'drawCopyBits';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT] = 'drawText';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE] = 'drawStroke';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3] = 'drawRop3';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS] = 'drawInvers';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE] = 'handleStreamCreate';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY] = 'handleStreamDestroy';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA] = 'handleStreamData';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP] = 'handleStreamClip';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND] = 'drawBlend';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST] = 'invalList';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES] = 'invalPalettes';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK] = 'displayMark';
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET] = 'displayReset';
|
||||
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_BITMAP] = 'bitmap';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC] = 'quic';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_RESERVED] = 'reserved';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_PNG] = 'png';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_PLT] = 'lz_plt';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB] = 'lz_rgb';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_GLZ_RGB] = 'glz_rgb';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_FROM_CACHE] = 'cache';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_SURFACE] = 'surface';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_JPEG] = 'jpeg';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS] = 'cache_lossless';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB] = 'zlib_glz_rgb';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_JPEG_ALPHA] = 'jpeg_alpha';
|
||||
this.imageTypes[wdi.SpiceImageType.SPICE_IMAGE_TYPE_CANVAS] = 'canvas';
|
||||
},
|
||||
|
||||
setStartTime: function (time) {
|
||||
this.startTimes.push(time);
|
||||
},
|
||||
|
||||
getSpiceMessageType: function (spiceMessage, prepend, append) {
|
||||
var type = this.routeList[spiceMessage.messageType];
|
||||
|
||||
if (type === 'drawImage') {
|
||||
type += '_' + this.imageTypes[spiceMessage.args.image.imageDescriptor.type];
|
||||
}
|
||||
|
||||
return (prepend || '') + type + (append || '');
|
||||
},
|
||||
|
||||
setNetworkTimeStart: function (time) {
|
||||
this.networkStart = this.networkStart || time || Date.now();
|
||||
},
|
||||
|
||||
logNetworkTime: function () {
|
||||
if (this.networkStart) {
|
||||
this.networkTotalTime += Date.now() - this.networkStart;
|
||||
this.networkStart = 0;
|
||||
}
|
||||
},
|
||||
|
||||
startTestSession: function () {
|
||||
this.clear();
|
||||
wdi.logOperations = true;
|
||||
this.testStartTime = Date.now();
|
||||
},
|
||||
|
||||
stopTestSession: function () {
|
||||
this.testStopTime = Date.now();
|
||||
wdi.logOperations = false;
|
||||
},
|
||||
|
||||
log: function(spiceMessage, start, customType, useTimeQueue, prepend, append) {
|
||||
var end = Date.now();
|
||||
var type;
|
||||
if(customType) {
|
||||
type = customType;
|
||||
} else {
|
||||
type = this.getSpiceMessageType(spiceMessage, prepend, append);
|
||||
}
|
||||
|
||||
if(!this.data.hasOwnProperty(type)) {
|
||||
this.data[type] = [];
|
||||
}
|
||||
|
||||
if (useTimeQueue) {
|
||||
start = this.startTimes.shift();
|
||||
}
|
||||
|
||||
this.data[type].push({start: start, end: end});
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.data = {};
|
||||
this.testStartTime = 0;
|
||||
this.testStopTime = 0;
|
||||
this.networkTotalTime = 0;
|
||||
this.networkStart = 0;
|
||||
},
|
||||
|
||||
getData: function() {
|
||||
return this.data;
|
||||
},
|
||||
|
||||
getStats: function() {
|
||||
var networkTime = this.networkTotalTime;
|
||||
var numOperations = 0;
|
||||
var totalTimeSpent = networkTime;
|
||||
var totalTime = this.testStopTime - this.testStartTime;
|
||||
|
||||
var dataSource = this.data;
|
||||
var partialTimes = {};
|
||||
var result = "";
|
||||
var data;
|
||||
|
||||
for(var i in this.data) {
|
||||
if(this.data.hasOwnProperty(i)) {
|
||||
data = dataSource[i];
|
||||
numOperations += data.length;
|
||||
partialTimes[i] = 0;
|
||||
for(var x = 0;x< data.length;x++) {
|
||||
partialTimes[i] += data[x].end - data[x].start;
|
||||
}
|
||||
totalTimeSpent += partialTimes[i];
|
||||
}
|
||||
}
|
||||
|
||||
result += "Total operations by number:\n";
|
||||
|
||||
var partial = 0;
|
||||
for(var i in dataSource) {
|
||||
if(dataSource.hasOwnProperty(i)) {
|
||||
partial = (dataSource[i].length / numOperations) * 100;
|
||||
result += i+': '+(~~partial)+"% (" + dataSource[i].length + ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
result += "Total numOperations: " + numOperations + "\n";
|
||||
result += "---------------------------------\n";
|
||||
result += "\n";
|
||||
|
||||
result += "Total Operations by time:\n";
|
||||
|
||||
for(i in partialTimes) {
|
||||
if(partialTimes.hasOwnProperty(i)) {
|
||||
partial = (partialTimes[i] / totalTime) * 100;
|
||||
result += i+': '+(~~partial)+"% ("+partialTimes[i]+"ms)\n";
|
||||
}
|
||||
}
|
||||
|
||||
var idleTime = totalTime - totalTimeSpent;
|
||||
partial = (idleTime / totalTime) * 100;
|
||||
result += "Idle: "+(~~partial)+"% ("+idleTime+"ms)\n";
|
||||
partial = (networkTime / totalTime) * 100;
|
||||
result += "Network: " + (~~partial) + "% (" + networkTime + "ms)\n";
|
||||
|
||||
result += 'Total time: ' + totalTime + 'ms \n';
|
||||
|
||||
return "BEGIN OF PERFORMANCE STATS\n" + result + "\nEND OF PERFORMANCE STATS\n";
|
||||
}
|
||||
};
|
||||
|
||||
wdi.DataLogger.init();
|
126
js/spice-web-client/lib/GenericObjectPool.js
Normal file
126
js/spice-web-client/lib/GenericObjectPool.js
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
Generic Object Pooling from:
|
||||
https://github.com/miohtama/objectpool.js/
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2013 Mikko Ohtamaa
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Version: 65c7399c30a3f6f3593bb4bfca3d9cde65675b84 (git commit)
|
||||
*/
|
||||
|
||||
|
||||
wdi.GenericObjectPool = $.spcExtend(wdi.EventObject.prototype, {
|
||||
|
||||
/** How fast we grow */
|
||||
expandFactor : 0.2,
|
||||
|
||||
/** Minimum number of items we grow */
|
||||
expandMinUnits : 16,
|
||||
|
||||
elems : null,
|
||||
|
||||
/** List of discarded element indexes in our this.elems pool */
|
||||
freeElems : null,
|
||||
|
||||
allocator: null,
|
||||
resetor: null,
|
||||
|
||||
/**
|
||||
* Generic object pool for Javascript.
|
||||
*
|
||||
* @param {Function} allocator return new empty elements
|
||||
*
|
||||
* @param {Function} resetor resetor(obj, index) is called on all new elements when they are (re)allocated from pool.
|
||||
* This is mostly useful for making object to track its own pool index.
|
||||
*/
|
||||
init : function(params) {
|
||||
var allocator = params[0];
|
||||
var resetor = params[1];
|
||||
// Start with one element
|
||||
this.allocator = allocator;
|
||||
this.resetor = resetor;
|
||||
// Set initial state of 1 object
|
||||
this.elems = [this.allocator()];
|
||||
this.freeElems = [0];
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
create : function() {
|
||||
|
||||
if(!this.freeElems.length) {
|
||||
this.expand();
|
||||
}
|
||||
|
||||
// See if we have any allocated elements to reuse
|
||||
var index = this.freeElems.pop();
|
||||
var elem = this.elems[index];
|
||||
this.resetor(elem, index);
|
||||
return elem;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* How many allocated units we have
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
length : function() {
|
||||
return this.elems.length - this.freeElems.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make pool bigger by the default growth parameters.
|
||||
*
|
||||
*/
|
||||
expand : function() {
|
||||
|
||||
var oldSize = this.elems.length;
|
||||
|
||||
var growth = Math.ceil(this.elems.length * this.expandFactor);
|
||||
|
||||
if(growth < this.expandMinUnits) {
|
||||
growth = this.expandMinUnits;
|
||||
}
|
||||
|
||||
this.elems.length = this.elems.length + growth;
|
||||
|
||||
for(var i=oldSize; i<this.elems.length; i++) {
|
||||
this.elems[i] = this.allocator();
|
||||
this.freeElems.push(i);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deallocate object at index n
|
||||
*
|
||||
* @param {Number} n
|
||||
* @return {Object} discarded object
|
||||
*/
|
||||
discard : function(n) {
|
||||
|
||||
// Cannot double deallocate
|
||||
if(this.freeElems.indexOf(n) >= 0) {
|
||||
throw "GeneircObjectPool: Double-free for element index: "+n;
|
||||
}
|
||||
|
||||
if(this.elems[n].keepAlive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.freeElems.push(n);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return object at pool index n
|
||||
*/
|
||||
get : function(n) {
|
||||
return this.elems[n];
|
||||
}
|
||||
});
|
114
js/spice-web-client/lib/GlobalPool.js
Normal file
114
js/spice-web-client/lib/GlobalPool.js
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.GlobalPool = {
|
||||
pools: {},
|
||||
retained: null,
|
||||
init: function() {
|
||||
this.retained = {};
|
||||
var self = this;
|
||||
this.pools['ViewQueue'] = new wdi.GenericObjectPool([function() {
|
||||
//factory
|
||||
return new wdi.ViewQueue();
|
||||
}, function(obj, index) {
|
||||
//reset
|
||||
obj.poolIndex = index; //update index at pool
|
||||
obj.setData([]); //reset the object
|
||||
}]);
|
||||
|
||||
this.pools['RawSpiceMessage'] = new wdi.GenericObjectPool([function() {
|
||||
//factory
|
||||
return new wdi.RawSpiceMessage();
|
||||
}, function(obj, index) {
|
||||
//reset
|
||||
obj.poolIndex = index; //update index at pool
|
||||
obj.set(null, null, null); //reset the object
|
||||
}]);
|
||||
|
||||
this.retained['Image'] = [];
|
||||
this.pools['Image'] = new wdi.GenericObjectPool([function() {
|
||||
//factory
|
||||
return new Image();
|
||||
}, function(obj, index) {
|
||||
//reset
|
||||
obj.poolIndex = index;
|
||||
obj.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';//Blank image 1x1 pixel (avoids console error GET null image)
|
||||
obj.onload = null;
|
||||
obj.keepAlive = false;
|
||||
self.retained['Image'][index] = obj;
|
||||
}]);
|
||||
|
||||
|
||||
this.retained['Canvas'] = [];
|
||||
this.pools['Canvas'] = new wdi.GenericObjectPool([function() {
|
||||
//factory
|
||||
return self.createCanvas();
|
||||
}, function(obj, index) {
|
||||
//reset
|
||||
obj.keepAlive = false;
|
||||
//obj.getContext('2d').clearRect(0, 0, obj.width, obj.height);
|
||||
obj.poolIndex = index;
|
||||
self.retained['Canvas'][index] = obj;
|
||||
}]);
|
||||
},
|
||||
|
||||
createCanvas: function() {
|
||||
return $('<canvas/>')[0];
|
||||
},
|
||||
|
||||
create: function(objectType) {
|
||||
return this.pools[objectType].create();
|
||||
},
|
||||
|
||||
discard: function(objectType, obj) {
|
||||
//check if its an autorelease pool
|
||||
if(this.retained.hasOwnProperty(objectType)) {
|
||||
delete this.retained[objectType][obj.poolIndex];
|
||||
}
|
||||
return this.pools[objectType].discard(obj.poolIndex);
|
||||
},
|
||||
|
||||
cleanPool: function(objectType) {
|
||||
|
||||
if(this.retained.hasOwnProperty(objectType)) {
|
||||
var pool = this.pools[objectType];
|
||||
|
||||
for(var i in this.retained[objectType]) {
|
||||
if(pool.discard(this.retained[objectType][i].poolIndex)) {
|
||||
delete this.retained[objectType][i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wdi.Debug.error("GlobalPool: cleanPool called with invalid objectType: ",objectType);
|
||||
}
|
||||
}
|
||||
}
|
141
js/spice-web-client/lib/ImageUncompressor.js
Normal file
141
js/spice-web-client/lib/ImageUncompressor.js
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.ImageUncompressor = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function (c) {
|
||||
this.syncAsyncHandler = c.syncAsyncHandler || new wdi.SyncAsyncHandler({
|
||||
isAsync: c.isAsync
|
||||
});
|
||||
},
|
||||
|
||||
lzHeaderSize: 32,
|
||||
|
||||
extractLzHeader: function (imageData, brush) {
|
||||
var headerData, header;
|
||||
if (!brush) { //brushes are still js arrays
|
||||
if (Object.prototype.toString.call(imageData) === "[object Array]") {
|
||||
headerData = imageData.slice(0, this.lzHeaderSize);
|
||||
imageData = imageData.slice(this.lzHeaderSize); //skip the header
|
||||
} else {
|
||||
headerData = imageData.subarray(0, this.lzHeaderSize).toJSArray();
|
||||
imageData = imageData.subarray(this.lzHeaderSize); //skip the header
|
||||
}
|
||||
header = wdi.LZSS.demarshall_rgb(headerData);
|
||||
} else {
|
||||
header = wdi.LZSS.demarshall_rgb(imageData);
|
||||
}
|
||||
|
||||
return {
|
||||
header: header,
|
||||
imageData: imageData
|
||||
};
|
||||
},
|
||||
|
||||
processLz: function (imageData, brush, opaque, clientGui, callback, scope) {
|
||||
var extractedData, u8, buffer, number, context;
|
||||
|
||||
extractedData = this.extractLzHeader(imageData, brush);
|
||||
imageData = extractedData.imageData;
|
||||
number = extractedData.header.width * extractedData.header.height * 4;
|
||||
|
||||
buffer = new ArrayBuffer(imageData.length + 16);
|
||||
u8 = new Uint8Array(buffer);
|
||||
|
||||
u8[0] = 1; //LZ_RGB
|
||||
u8[1] = opaque;
|
||||
u8[2] = extractedData.header.type;
|
||||
u8[3] = extractedData.header.top_down; //padding
|
||||
|
||||
for (var i = 0; i < 4; i++) { //iterations because of javascript number size
|
||||
u8[4 + i] = number & (255); //Get only the last byte
|
||||
number = number >> 8; //Remove the last byte
|
||||
}
|
||||
|
||||
var view = new DataView(buffer);
|
||||
view.setUint32(8, extractedData.header.width);
|
||||
view.setUint32(12, extractedData.header.height);
|
||||
|
||||
u8.set(imageData, 16);
|
||||
|
||||
this.syncAsyncHandler.dispatch(buffer, callback, scope);
|
||||
},
|
||||
|
||||
processQuic: function (imageData, opaque, clientGui, callback, scope) {
|
||||
wdi.Debug.log('Quic decode');
|
||||
buffer = new ArrayBuffer(imageData.length + 4);
|
||||
view = new Uint8Array(buffer);
|
||||
|
||||
view.set(imageData, 4);
|
||||
view[3] = opaque ? 1 : 0;
|
||||
view[0] = 0; //quic
|
||||
|
||||
this.syncAsyncHandler.dispatch(buffer, callback, scope);
|
||||
},
|
||||
|
||||
process: function (imageDescriptor, imageData, brush, opaque, clientGui, callback, scope) {
|
||||
switch(imageDescriptor.type) {
|
||||
case wdi.SpiceImageType.SPICE_IMAGE_TYPE_QUIC:
|
||||
this.processQuic(imageData, opaque, clientGui, callback, scope);
|
||||
break;
|
||||
case wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_RGB:
|
||||
this.processLz(imageData, brush, opaque, clientGui, callback, scope);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.syncAsyncHandler.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
var syncInstance;
|
||||
var asyncInstance;
|
||||
|
||||
wdi.ImageUncompressor.getSyncInstance = function () {
|
||||
if (!syncInstance) {
|
||||
syncInstance = new wdi.ImageUncompressor({
|
||||
isAsync: false
|
||||
});
|
||||
}
|
||||
|
||||
return syncInstance;
|
||||
};
|
||||
|
||||
wdi.ImageUncompressor.getAsyncInstance = function () {
|
||||
if (!asyncInstance) {
|
||||
asyncInstance = new wdi.ImageUncompressor({
|
||||
isAsync: true
|
||||
});
|
||||
}
|
||||
|
||||
return asyncInstance;
|
||||
};
|
77
js/spice-web-client/lib/IntegrationBenchmark.js
Normal file
77
js/spice-web-client/lib/IntegrationBenchmark.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.IntegrationBenchmark = {
|
||||
benchmarking: false,
|
||||
startTime: 0,
|
||||
timeoutInterval: 3000, // in ms, amount of time after it will be considered that
|
||||
// we have received all packets and can stop counting
|
||||
timeOutId: undefined,
|
||||
|
||||
busConnection: undefined,
|
||||
|
||||
setEndTime: function() {
|
||||
var self = this;
|
||||
this.timeOutId = setTimeout(function() {
|
||||
// if 3000 ms have passed since the last packet we assume we have processed them all and can launch MS Word
|
||||
self.timeOutId = undefined;
|
||||
self.benchmarking = false;
|
||||
var now = new Date().getTime();
|
||||
var elapsed = now - self.startTime - self.timeoutInterval;
|
||||
self.onEndBenchmarkCallback(elapsed);
|
||||
var message = {
|
||||
"type": wdi.BUS_TYPES.killApplicationDoNotUseInProductionEver,
|
||||
"application": "EXCEL.EXE"
|
||||
};
|
||||
self.busConnection.send(message);
|
||||
}, this.timeoutInterval);
|
||||
},
|
||||
|
||||
setStartTime: function() {
|
||||
if (this.timeOutId !== undefined) {
|
||||
clearTimeout(this.timeOutId);
|
||||
}
|
||||
},
|
||||
|
||||
launchApp: function(busConnection, onEndBenchmarkCallback) {
|
||||
this.busConnection = busConnection;
|
||||
wdi.IntegrationBenchmark.benchmarking = true;
|
||||
wdi.IntegrationBenchmark.setStartTime();
|
||||
this.onEndBenchmarkCallback = onEndBenchmarkCallback;
|
||||
this.startTime = new Date().getTime();
|
||||
var message = {
|
||||
"type": wdi.BUS_TYPES.launchApplication,
|
||||
"file": "c:\\Users\\eyeos\\Desktop\\test.xlsx"
|
||||
};
|
||||
this.busConnection.send(message);
|
||||
}
|
||||
};
|
116
js/spice-web-client/lib/PacketWorkerIdentifier.js
Normal file
116
js/spice-web-client/lib/PacketWorkerIdentifier.js
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check if a packet should be intercepted in packetpreprocess to be executed
|
||||
* in parallel.
|
||||
*/
|
||||
|
||||
wdi.PacketWorkerIdentifier = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function(c) {
|
||||
//default empty constructor
|
||||
},
|
||||
|
||||
shouldUseWorker: function(message) {
|
||||
switch (message.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
|
||||
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
|
||||
var brush = message.args.brush;
|
||||
if(brush.type === wdi.SpiceBrushType.SPICE_BRUSH_TYPE_PATTERN) {
|
||||
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
|
||||
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
|
||||
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
|
||||
return wdi.PacketWorkerIdentifier.processingType.DECOMPRESS;
|
||||
//case wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA:
|
||||
// return wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
||||
getImageProperties: function(message) {
|
||||
var props = {
|
||||
data: null,
|
||||
descriptor: null,
|
||||
opaque: true,
|
||||
brush: null
|
||||
};
|
||||
|
||||
//coupling here, to be cleaned when doing real code
|
||||
switch (message.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY:
|
||||
props.descriptor = message.args.image.imageDescriptor;
|
||||
props.data = message.args.image.data;
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL:
|
||||
props.brush = message.args.brush;
|
||||
if(props.brush.type === wdi.SpiceBrushType.SPICE_BRUSH_TYPE_PATTERN) {
|
||||
props.descriptor = props.brush.pattern.image;
|
||||
props.data = props.brush.pattern.imageData;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND:
|
||||
case wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:
|
||||
props.data = message.args.image.data;
|
||||
props.descriptor = message.args.image.imageDescriptor;
|
||||
props.opaque = false;
|
||||
break;
|
||||
default:
|
||||
wdi.Debug.log("PacketWorkerIdentifier: Unknown Packet in getImageProperties");
|
||||
return false;
|
||||
}
|
||||
|
||||
return props;
|
||||
},
|
||||
|
||||
getVideoData: function(message) {
|
||||
if(message.messageType !== wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA) {
|
||||
wdi.Debug.log('PacketWOrkerIdentifier: Invalid packet in getVideoData');
|
||||
return false;
|
||||
}
|
||||
|
||||
return message.args.data;
|
||||
}
|
||||
});
|
||||
|
||||
wdi.PacketWorkerIdentifier.processingType = {};
|
||||
wdi.PacketWorkerIdentifier.processingType.DECOMPRESS = 1;
|
||||
wdi.PacketWorkerIdentifier.processingType.PROCESSVIDEO = 2;
|
57
js/spice-web-client/lib/SyncAsyncHandler.js
Normal file
57
js/spice-web-client/lib/SyncAsyncHandler.js
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SyncAsyncHandler = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function (c) {
|
||||
this.isAsync = !!c.isAsync;
|
||||
if (this.isAsync) {
|
||||
this.asyncWorker = c.asyncWorker || new wdi.AsyncWorker({script:'application/WorkerProcess.js'});
|
||||
}
|
||||
},
|
||||
|
||||
isAsync: null,
|
||||
|
||||
dispatch: function(buffer, callback, scope) {
|
||||
if (this.isAsync) {
|
||||
this.asyncWorker.run(buffer, callback, scope);
|
||||
} else {
|
||||
var result = window['workerDispatch'](buffer, this.isAsync);
|
||||
callback.call(scope, result);
|
||||
}
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
if (this.isAsync) {
|
||||
this.asyncWorker.dispose();
|
||||
}
|
||||
}
|
||||
});
|
279
js/spice-web-client/lib/base64.js
Normal file
279
js/spice-web-client/lib/base64.js
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Modified from:
|
||||
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
|
||||
*/
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla XML-RPC Client component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Digital Creations 2, Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Martijn Pieters <mj@digicool.com> (original author)
|
||||
* Samuel Sieb <samuel@sieb.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint white: false, bitwise: false, plusplus: false */
|
||||
/*global console */
|
||||
|
||||
var Base64 = {
|
||||
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
||||
base64Pad : '=',
|
||||
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '',
|
||||
chrTable = Base64.toBase64Table.split(''),
|
||||
pad = Base64.base64Pad,
|
||||
length = data.length,
|
||||
i;
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
for (i = 0; i < (length - 2); i += 3) {
|
||||
result += chrTable[data[i] >> 2];
|
||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||
result += chrTable[data[i+2] & 0x3f];
|
||||
}
|
||||
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
if (length%3) {
|
||||
i = length - (length%3);
|
||||
result += chrTable[data[i] >> 2];
|
||||
if ((length%3) === 2) {
|
||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += chrTable[(data[i+1] & 0x0f) << 2];
|
||||
result += pad;
|
||||
} else {
|
||||
result += chrTable[(data[i] & 0x03) << 4];
|
||||
result += pad + pad;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/* Convert Base64 data to a string */
|
||||
toBinaryTable : [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var binTable = Base64.toBinaryTable,
|
||||
pad = Base64.base64Pad,
|
||||
result, result_length, idx, i, c, padding,
|
||||
leftbits = 0, // number of bits decoded, but yet to be appended
|
||||
leftdata = 0, // bits decoded, but yet to be appended
|
||||
data_length = data.indexOf('=') - offset;
|
||||
|
||||
if (data_length < 0) { data_length = data.length - offset; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5);
|
||||
result = new Array(result_length);
|
||||
|
||||
// Convert one by one.
|
||||
for (idx = 0, i = offset; i < data.length; i++) {
|
||||
c = binTable[data.charCodeAt(i) & 0x7f];
|
||||
padding = (data.charAt(i) === pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c === -1) {
|
||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding) {
|
||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||
}
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits) {
|
||||
throw {name: 'Base64-Error',
|
||||
message: 'Corrupted base64 string'};
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// private property
|
||||
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
// public method for encoding
|
||||
encodeStr : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = Base64._utf8_encode(input);
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output = output +
|
||||
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
|
||||
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
// public method for decoding
|
||||
decodeStr : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output = Base64._utf8_decode(output);
|
||||
|
||||
return output;
|
||||
|
||||
},
|
||||
|
||||
// private method for UTF-8 encoding
|
||||
_utf8_encode : function (string) {
|
||||
string = string.replace(/\r\n/g,"\n");
|
||||
var utftext = "";
|
||||
|
||||
for (var n = 0; n < string.length; n++) {
|
||||
|
||||
var c = string.charCodeAt(n);
|
||||
|
||||
if (c < 128) {
|
||||
utftext += String.fromCharCode(c);
|
||||
}
|
||||
else if((c > 127) && (c < 2048)) {
|
||||
utftext += String.fromCharCode((c >> 6) | 192);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
else {
|
||||
utftext += String.fromCharCode((c >> 12) | 224);
|
||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||
utftext += String.fromCharCode((c & 63) | 128);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return utftext;
|
||||
},
|
||||
|
||||
// private method for UTF-8 decoding
|
||||
_utf8_decode : function (utftext) {
|
||||
var string = "";
|
||||
var i = 0;
|
||||
var c = c1 = c2 = 0;
|
||||
|
||||
while ( i < utftext.length ) {
|
||||
|
||||
c = utftext.charCodeAt(i);
|
||||
|
||||
if (c < 128) {
|
||||
string += String.fromCharCode(c);
|
||||
i++;
|
||||
}
|
||||
else if((c > 191) && (c < 224)) {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
c2 = utftext.charCodeAt(i+1);
|
||||
c3 = utftext.charCodeAt(i+2);
|
||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
}; /* End of Base64 namespace */
|
1621
js/spice-web-client/lib/biginteger.js
Normal file
1621
js/spice-web-client/lib/biginteger.js
Normal file
File diff suppressed because it is too large
Load Diff
257
js/spice-web-client/lib/bowser.js
Normal file
257
js/spice-web-client/lib/bowser.js
Normal file
@ -0,0 +1,257 @@
|
||||
// Source: https://github.com/ded/bowser
|
||||
|
||||
/**
|
||||
* README!!!!
|
||||
* To solve the error "MISMATCHED ANONYMOUS DEFINE() MODULES ..." (http://requirejs.org/docs/errors.html)
|
||||
* It happens when requirejs is defined globally and this script is added
|
||||
* in the old fashioned way, i.e. with <script src="..."></script>
|
||||
* -> comment just the follow line if it happens!!
|
||||
* else if (typeof define == 'function') define(definition)
|
||||
*/
|
||||
|
||||
!function (name, definition) {
|
||||
if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition()
|
||||
/*
|
||||
* problems including this library as classig <script src="..."> in a require
|
||||
* environment, so commenting out the following line (as stated above) while
|
||||
* we prepare a better fix involving requirejs shim.
|
||||
* @kampde, 2014-10-02
|
||||
*/
|
||||
//else if (typeof define == 'function') define(definition)
|
||||
else this[name] = definition()
|
||||
}('bowser', function () {
|
||||
/**
|
||||
* See useragents.js for examples of navigator.userAgent
|
||||
*/
|
||||
|
||||
var t = true
|
||||
|
||||
function detect(ua) {
|
||||
|
||||
function getFirstMatch(regex) {
|
||||
var match = ua.match(regex);
|
||||
return (match && match.length > 1 && match[1]) || '';
|
||||
}
|
||||
|
||||
var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase()
|
||||
, likeAndroid = /like android/i.test(ua)
|
||||
, android = !likeAndroid && /android/i.test(ua)
|
||||
, versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i)
|
||||
, tablet = /tablet/i.test(ua)
|
||||
, mobile = !tablet && /[^-]mobi/i.test(ua)
|
||||
, result
|
||||
|
||||
if (/opera|opr/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Opera'
|
||||
, opera: t
|
||||
, version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/windows phone/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Windows Phone'
|
||||
, windowsphone: t
|
||||
, msie: t
|
||||
, version: getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/msie|trident/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Internet Explorer'
|
||||
, msie: t
|
||||
, version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/chrome|crios|crmo/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Chrome'
|
||||
, chrome: t
|
||||
, version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (iosdevice) {
|
||||
result = {
|
||||
name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod'
|
||||
}
|
||||
// WTF: version is not part of user agent in web apps
|
||||
if (versionIdentifier) {
|
||||
result.version = versionIdentifier
|
||||
}
|
||||
}
|
||||
else if (/sailfish/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Sailfish'
|
||||
, sailfish: t
|
||||
, version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/seamonkey\//i.test(ua)) {
|
||||
result = {
|
||||
name: 'SeaMonkey'
|
||||
, seamonkey: t
|
||||
, version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/firefox|iceweasel/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Firefox'
|
||||
, firefox: t
|
||||
, version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i)
|
||||
}
|
||||
if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) {
|
||||
result.firefoxos = t
|
||||
}
|
||||
}
|
||||
else if (/silk/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Amazon Silk'
|
||||
, silk: t
|
||||
, version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (android) {
|
||||
result = {
|
||||
name: 'Android'
|
||||
, version: versionIdentifier
|
||||
}
|
||||
}
|
||||
else if (/phantom/i.test(ua)) {
|
||||
result = {
|
||||
name: 'PhantomJS'
|
||||
, phantom: t
|
||||
, version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) {
|
||||
result = {
|
||||
name: 'BlackBerry'
|
||||
, blackberry: t
|
||||
, version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
}
|
||||
else if (/(web|hpw)os/i.test(ua)) {
|
||||
result = {
|
||||
name: 'WebOS'
|
||||
, webos: t
|
||||
, version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)
|
||||
};
|
||||
/touchpad\//i.test(ua) && (result.touchpad = t)
|
||||
}
|
||||
else if (/bada/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Bada'
|
||||
, bada: t
|
||||
, version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i)
|
||||
};
|
||||
}
|
||||
else if (/tizen/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Tizen'
|
||||
, tizen: t
|
||||
, version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier
|
||||
};
|
||||
}
|
||||
else if (/safari/i.test(ua)) {
|
||||
result = {
|
||||
name: 'Safari'
|
||||
, safari: t
|
||||
, version: versionIdentifier
|
||||
}
|
||||
}
|
||||
else result = {}
|
||||
|
||||
// set webkit or gecko flag for browsers based on these engines
|
||||
if (/(apple)?webkit/i.test(ua)) {
|
||||
result.name = result.name || "Webkit"
|
||||
result.webkit = t
|
||||
if (!result.version && versionIdentifier) {
|
||||
result.version = versionIdentifier
|
||||
}
|
||||
} else if (!result.opera && /gecko\//i.test(ua)) {
|
||||
result.name = result.name || "Gecko"
|
||||
result.gecko = t
|
||||
result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i)
|
||||
}
|
||||
|
||||
// set OS flags for platforms that have multiple browsers
|
||||
if (android || result.silk) {
|
||||
result.android = t
|
||||
} else if (iosdevice) {
|
||||
result[iosdevice] = t
|
||||
result.ios = t
|
||||
}
|
||||
|
||||
// OS version extraction
|
||||
var osVersion = '';
|
||||
if (iosdevice) {
|
||||
osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i);
|
||||
osVersion = osVersion.replace(/[_\s]/g, '.');
|
||||
} else if (android) {
|
||||
osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i);
|
||||
} else if (result.windowsphone) {
|
||||
osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i);
|
||||
} else if (result.webos) {
|
||||
osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i);
|
||||
} else if (result.blackberry) {
|
||||
osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i);
|
||||
} else if (result.bada) {
|
||||
osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i);
|
||||
} else if (result.tizen) {
|
||||
osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i);
|
||||
}
|
||||
if (osVersion) {
|
||||
result.osversion = osVersion;
|
||||
}
|
||||
|
||||
// device type extraction
|
||||
var osMajorVersion = osVersion.split('.')[0];
|
||||
if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) {
|
||||
result.tablet = t
|
||||
} else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) {
|
||||
result.mobile = t
|
||||
}
|
||||
|
||||
// Graded Browser Support
|
||||
// http://developer.yahoo.com/yui/articles/gbs
|
||||
if ((result.msie && result.version >= 9) ||
|
||||
(result.chrome && result.version >= 20) ||
|
||||
(result.firefox && result.version >= 10.0) ||
|
||||
(result.safari && result.version >= 5) ||
|
||||
(result.opera && result.version >= 10.0) ||
|
||||
(result.ios && result.osversion && result.osversion.split(".")[0] >= 6)
|
||||
) {
|
||||
result.a = t;
|
||||
}
|
||||
else if ((result.msie && result.version < 9) ||
|
||||
(result.chrome && result.version < 20) ||
|
||||
(result.firefox && result.version < 10.0) ||
|
||||
(result.safari && result.version < 5) ||
|
||||
(result.opera && result.version < 10.0) ||
|
||||
(result.ios && result.osversion && result.osversion.split(".")[0] < 6)
|
||||
) {
|
||||
result.c = t
|
||||
} else result.x = t
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '')
|
||||
|
||||
|
||||
/*
|
||||
* Set our detect method to the main bowser object so we can
|
||||
* reuse it to test other user agents.
|
||||
* This is needed to implement future tests.
|
||||
*/
|
||||
bowser._detect = detect;
|
||||
|
||||
/**
|
||||
* README: all_compiled uses window["bowser]
|
||||
*/
|
||||
if (!window['bowser']) {
|
||||
window['bowser'] = bowser;
|
||||
}
|
||||
|
||||
return bowser
|
||||
});
|
84
js/spice-web-client/lib/displayRouter.js
Normal file
84
js/spice-web-client/lib/displayRouter.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.DisplayRouter = $.spcExtend(wdi.EventObject.prototype, {
|
||||
|
||||
init: function(c) {
|
||||
this.clientGui = c.clientGui;
|
||||
this.rasterEngine = c.rasterEngine || new wdi.RasterEngine({clientGui: this.clientGui});
|
||||
if(c.routeList) {
|
||||
this.routeList = c.routeList;
|
||||
} else {
|
||||
this._initRoutes();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_initRoutes: function() {
|
||||
this.routeList = {};
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_CREATE] = this.rasterEngine.drawCanvas;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_SURFACE_DESTROY] = this.rasterEngine.removeCanvas;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_COPY] = this.rasterEngine.drawImage;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_FILL] = this.rasterEngine.drawFill;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND] = this.rasterEngine.drawAlphaBlend;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_WHITENESS] = this.rasterEngine.drawWhiteness;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLACKNESS] = this.rasterEngine.drawBlackness;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TRANSPARENT] = this.rasterEngine.drawTransparent;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS] = this.rasterEngine.drawCopyBits;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_TEXT] = this.rasterEngine.drawText;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_STROKE] = this.rasterEngine.drawStroke;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_ROP3] = this.rasterEngine.drawRop3;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_INVERS] = this.rasterEngine.drawInvers;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CREATE] = this.rasterEngine.handleStreamCreate;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DESTROY] = this.rasterEngine.handleStreamDestroy;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_DATA] = this.rasterEngine.handleStreamData;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_STREAM_CLIP] = this.rasterEngine.handleStreamClip;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_DRAW_BLEND] = this.rasterEngine.drawBlend;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_LIST] = this.rasterEngine.invalList;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES] = this.rasterEngine.invalPalettes;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_MARK] = false;
|
||||
this.routeList[wdi.SpiceVars.SPICE_MSG_DISPLAY_RESET] = false;
|
||||
},
|
||||
|
||||
processPacket: function(spiceMessage) {
|
||||
//filter out empty messages
|
||||
if(!spiceMessage) {
|
||||
wdi.Debug.log('DisplayProcess processPacket: Skipping empty message...');
|
||||
return;
|
||||
}
|
||||
|
||||
var route = this.routeList[spiceMessage.messageType];
|
||||
if (route) {
|
||||
route.call(this.rasterEngine, spiceMessage);
|
||||
}
|
||||
}
|
||||
});
|
172
js/spice-web-client/lib/encrypt.js
Normal file
172
js/spice-web-client/lib/encrypt.js
Normal file
@ -0,0 +1,172 @@
|
||||
rng = new SecureRandom();
|
||||
|
||||
function pack(source)
|
||||
|
||||
{
|
||||
|
||||
var temp = "";
|
||||
|
||||
for (var i = 0; i < source.length; i+=2)
|
||||
|
||||
{
|
||||
|
||||
temp+= String.fromCharCode(parseInt(source.substring(i, i + 2), 16));
|
||||
|
||||
}
|
||||
|
||||
return temp;
|
||||
|
||||
}
|
||||
function char2hex(source)
|
||||
|
||||
{
|
||||
|
||||
var hex = "";
|
||||
|
||||
for (var i = 0; i < source.length; i+=1)
|
||||
|
||||
{
|
||||
|
||||
temp = source[i].toString(16);
|
||||
|
||||
switch (temp.length)
|
||||
|
||||
{
|
||||
|
||||
case 1:
|
||||
|
||||
temp = "0" + temp;
|
||||
|
||||
break;
|
||||
|
||||
case 0:
|
||||
|
||||
temp = "00";
|
||||
|
||||
}
|
||||
|
||||
hex+= temp;
|
||||
|
||||
}
|
||||
|
||||
return hex;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function xor(a, b)
|
||||
|
||||
{
|
||||
|
||||
length = Math.min(a.length, b.length);
|
||||
|
||||
temp = "";
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
|
||||
{
|
||||
|
||||
temp+= String.fromCharCode(a.charCodeAt(i) ^ b.charCodeAt(i));
|
||||
|
||||
}
|
||||
|
||||
length = Math.max(a.length, b.length) - length;
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
|
||||
{
|
||||
|
||||
temp+= "\x00";
|
||||
|
||||
}
|
||||
|
||||
return temp;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mgf1(mgfSeed, maskLen)
|
||||
|
||||
{
|
||||
|
||||
t = "";
|
||||
|
||||
hLen = 20;
|
||||
|
||||
count = Math.ceil(maskLen / hLen);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
|
||||
{
|
||||
|
||||
c = String.fromCharCode((i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
|
||||
|
||||
t+= pack(sha1Hash(mgfSeed + c));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return t.substring(0, maskLen);
|
||||
|
||||
}
|
||||
function rsa_oaep_encrypt(message, n, e) {
|
||||
|
||||
// precomputed values
|
||||
var k = 128; // length of n in bytes
|
||||
var hLen = 20;
|
||||
var mLen = message.length;
|
||||
var lHash = '\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09'; // pack(sha1Hash(""))
|
||||
var temp = k - mLen - 2 * hLen - 2;
|
||||
|
||||
for (var i = 0; i < temp; i++) {
|
||||
lHash += '\x00';
|
||||
}
|
||||
|
||||
var db = lHash + '\x01' + message;
|
||||
|
||||
var seed = '';
|
||||
for (var i = 0; i < hLen + 4; i += 4) {
|
||||
temp = new Array(4);
|
||||
rng.nextBytes(temp);
|
||||
seed += String.fromCharCode(temp[0], temp[1], temp[2], temp[3]);
|
||||
}
|
||||
seed = seed.substring(4 - seed.length % 4);
|
||||
|
||||
var dbMask = mgf1(seed, k - hLen - 1);
|
||||
var maskedDB = xor(db, dbMask);
|
||||
var seedMask = mgf1(maskedDB, hLen);
|
||||
var maskedSeed = xor(seed, seedMask);
|
||||
var em = "\x00" + maskedSeed + maskedDB;
|
||||
|
||||
m = new Array();
|
||||
for (i = 0; i < em.length; i++) {
|
||||
m[i] = em.charCodeAt(i);
|
||||
}
|
||||
m = new encryptionBigInteger(m, 256);
|
||||
c = m.modPowInt(e, n); // doPublic
|
||||
c = c.toString(16);
|
||||
|
||||
if (c.length & 1)
|
||||
c = "0" + c;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
function RSA_public_encrypt(password, pub_key) {
|
||||
|
||||
var keyInChar = new Uint8Array(pub_key);
|
||||
var rawPubKey = new Array(129); // 00xxx
|
||||
|
||||
for (var i = 0; i < 129; i++)
|
||||
rawPubKey[i] = keyInChar[28 + i];
|
||||
|
||||
var n = new encryptionBigInteger(rawPubKey);
|
||||
var e = new encryptionBigInteger('010001', 16);
|
||||
|
||||
var hexRsa = rsa_oaep_encrypt(password + String.fromCharCode(0), n, e);
|
||||
return hexRsa;
|
||||
}
|
52
js/spice-web-client/lib/flipper.js
Normal file
52
js/spice-web-client/lib/flipper.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.Flipper = {
|
||||
|
||||
flip: function(sourceImg) {
|
||||
return this._handMadeFlip(sourceImg);
|
||||
},
|
||||
|
||||
_handMadeFlip: function(sourceImg) {
|
||||
var newCanvas = document.createElement('canvas');
|
||||
newCanvas.width = sourceImg.width;
|
||||
newCanvas.height = sourceImg.height;
|
||||
var ctx = newCanvas.getContext('2d');
|
||||
ctx.save();
|
||||
// Multiply the y value by -1 to flip vertically
|
||||
ctx.scale(1, -1);
|
||||
// Start at (0, -height), which is now the bottom-left corner
|
||||
ctx.drawImage(sourceImg, 0, -sourceImg.height);
|
||||
ctx.restore();
|
||||
return newCanvas;
|
||||
}
|
||||
};
|
1232
js/spice-web-client/lib/graphic.js
Normal file
1232
js/spice-web-client/lib/graphic.js
Normal file
File diff suppressed because it is too large
Load Diff
224
js/spice-web-client/lib/graphicdebug.js
Normal file
224
js/spice-web-client/lib/graphicdebug.js
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.GraphicDebug = $.spcExtend(wdi.DomainObject, {
|
||||
debugMode: null,
|
||||
spiceGraphicMessageTypes: [],
|
||||
cloneSpiceMessage: null,
|
||||
clientGui: null,
|
||||
tmpCanvas: null,
|
||||
tmpContext: null,
|
||||
originalCanvas: null,
|
||||
spiceMessageData: null,
|
||||
endCanvas: null,
|
||||
currentOperation: null,
|
||||
|
||||
init: function(c) {
|
||||
this.debugMode = c.debugMode;
|
||||
if (this.debugMode) {
|
||||
this._generateArray();
|
||||
this._showDebug();
|
||||
} else {
|
||||
this._hideDebug();
|
||||
}
|
||||
},
|
||||
|
||||
_generateArray: function() {
|
||||
var self = this;
|
||||
$.each(wdi.SpiceVars, function(key, value) {
|
||||
if (key.search("SPICE_MSG_DISPLAY_") != -1) {
|
||||
self.spiceGraphicMessageTypes[value] = key;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_showDebug: function() {
|
||||
$('#canvasSpace').show();
|
||||
$('#graphicDebug').show();
|
||||
},
|
||||
|
||||
_hideDebug: function() {
|
||||
$('#canvasSpace').hide();
|
||||
$('#graphicDebug').hide();
|
||||
},
|
||||
|
||||
printDebugMessageOnFilter: function(spiceMessage, clientGui) {
|
||||
if(spiceMessage.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY && this.debugMode && ($('#logActive').prop('checked'))) {
|
||||
var surface_id = null;
|
||||
this.clientGui = clientGui;
|
||||
this.cloneSpiceMessage = $.extend(true, {}, spiceMessage);
|
||||
if(this.cloneSpiceMessage.args.base && this.cloneSpiceMessage.args.base.surface_id !== null) {
|
||||
surface_id = this.cloneSpiceMessage.args.base.surface_id;
|
||||
var box = wdi.graphics.getBoxFromSrcArea(this.cloneSpiceMessage.args.base.box);
|
||||
var spiceMessageDiv = $('<div/>')
|
||||
.append(prettyPrint(this.cloneSpiceMessage))
|
||||
.hide();
|
||||
|
||||
this.originalCanvas = this._copyCanvasFromSurfaceId(surface_id);
|
||||
this.spiceMessageData = spiceMessage.args.originalData;
|
||||
this.currentOperation = this.spiceGraphicMessageTypes[this.cloneSpiceMessage.messageType];
|
||||
|
||||
$('#debugInfo')
|
||||
.append($('<br/>'))
|
||||
.append($('<hr/>'))
|
||||
.append(this._copyAndHighlightCanvas(surface_id, box))
|
||||
.append($('<br/>'))
|
||||
.append($('<div/>')
|
||||
.append(this.currentOperation + ' (Click to hide/show)')
|
||||
.css('cursor', 'pointer')
|
||||
.css('color', 'blue')
|
||||
.css('text-decoration', 'underline')
|
||||
.click(function() {
|
||||
spiceMessageDiv.toggle();
|
||||
})
|
||||
).append(spiceMessageDiv);
|
||||
|
||||
if (this.cloneSpiceMessage.args.hasOwnProperty('image')) {
|
||||
this._printImage(spiceMessageDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_printImage: function(spiceMessageDiv) {
|
||||
wdi.graphics.getImageFromSpice(this.cloneSpiceMessage.args.image.imageDescriptor, this.cloneSpiceMessage.args.image.data, this.clientGui, function(srcImg) {
|
||||
if(srcImg) {
|
||||
spiceMessageDiv.append(
|
||||
$('<div/>').css('font-size', '12px')
|
||||
.append('Image inside spiceMessage:')
|
||||
.append($('<br/>'))
|
||||
.css('border', '1px solid black')
|
||||
.append(srcImg)
|
||||
);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
printDebugMessageOnNotifyEnd: function(spiceMessage, clientGui) {
|
||||
this.clientGui = clientGui;
|
||||
if(spiceMessage.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY && this.debugMode && ($('#logActive').prop('checked'))) {
|
||||
var surface_id = null;
|
||||
if(spiceMessage.args.base && spiceMessage.args.base.surface_id !== null) {
|
||||
var self = this;
|
||||
var createTestClickCallback = function (currentSpiceMessage, originalCanvas, endCanvas, currentOperation) {
|
||||
return function () {
|
||||
self.createImageTest(currentSpiceMessage, originalCanvas, endCanvas, currentOperation);
|
||||
};
|
||||
};
|
||||
var createReplayClickCallback = function (currentSpiceMessage, originalCanvas, endCanvas, currentOperation) {
|
||||
return function () {
|
||||
self.createReplay(currentSpiceMessage, originalCanvas, endCanvas, currentOperation);
|
||||
};
|
||||
};
|
||||
surface_id = spiceMessage.args.base.surface_id;
|
||||
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.base.box);
|
||||
var currentCanvas = this._copyCanvasFromSurfaceId(surface_id);
|
||||
$('#debugInfo')
|
||||
.append($('<br/>'))
|
||||
.append($('<div/>')
|
||||
.append($('<button>Create test</button>')
|
||||
.css('cursor', 'pointer')
|
||||
.click(createTestClickCallback(this.spiceMessageData, this.originalCanvas, currentCanvas, this.currentOperation))
|
||||
)
|
||||
.append($('<button>Create replay window</button>')
|
||||
.css('cursor', 'pointer')
|
||||
.click(createReplayClickCallback(this.spiceMessageData, this.originalCanvas, currentCanvas, this.currentOperation))
|
||||
)
|
||||
).append($('<br/>'))
|
||||
.append(this._copyAndHighlightCanvas(surface_id, box));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
createImageTest: function (spiceMessage, originalCanvas, endCanvas, currentOperation) {
|
||||
var name = prompt('Name of the test', currentOperation);
|
||||
var data1 = originalCanvas.toDataURL('image/png');
|
||||
var data2 = endCanvas.toDataURL('image/png');
|
||||
var dataObj = {
|
||||
origin: data1,
|
||||
expected: data2,
|
||||
object: spiceMessage,
|
||||
name: name
|
||||
};
|
||||
var data = JSON.stringify(dataObj);
|
||||
var fileName = name.replace(/\s/g, '_');
|
||||
|
||||
$.post('graphictestgenerator.php','data=' + data + '&name=' + fileName).done(function (data,status,xhr) {
|
||||
alert('Test created');
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert('Test creation failed.\n\nGot response: ' + jqXHR.status + ' '
|
||||
+ jqXHR.statusText + '\n\n' + jqXHR.responseText);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
createReplay: function (spiceMessage, originalCanvas) {
|
||||
var data1 = originalCanvas.toDataURL('image/png');
|
||||
var dataObj = {
|
||||
origin: data1,
|
||||
object: spiceMessage,
|
||||
width: originalCanvas.width,
|
||||
height: originalCanvas.height
|
||||
};
|
||||
var data = JSON.stringify(dataObj);
|
||||
|
||||
$.post('graphictestgenerator.php','data=' + data + '&replay=true').done(function (data,status,xhr) {
|
||||
window.open('replay.html', 'replay');
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert('Replay failed.\n\nGot response: ' + jqXHR.status + ' '
|
||||
+ jqXHR.statusText + '\n\n' + jqXHR.responseText);
|
||||
});
|
||||
},
|
||||
|
||||
_copyCanvasFromSurfaceId: function (surface_id) {
|
||||
var context = this.clientGui.getContext(surface_id);
|
||||
this.tmpCanvas = context.canvas;
|
||||
var myCanvas = document.createElement('canvas');
|
||||
myCanvas.width = this.tmpCanvas.width;
|
||||
myCanvas.height = this.tmpCanvas.height;
|
||||
myCanvas.getContext('2d').drawImage(this.tmpCanvas, 0, 0);
|
||||
|
||||
return myCanvas;
|
||||
},
|
||||
|
||||
_copyAndHighlightCanvas: function(surface_id, box) {
|
||||
var myCanvas = this._copyCanvasFromSurfaceId(surface_id);
|
||||
|
||||
context = myCanvas.getContext('2d');
|
||||
|
||||
context.fillStyle = "rgba(255,0,0,0.3)";
|
||||
context.fillRect(box.x, box.y, box.width, box.height);
|
||||
return myCanvas;
|
||||
}
|
||||
|
||||
|
||||
});
|
173
js/spice-web-client/lib/images/bitmap.js
Normal file
173
js/spice-web-client/lib/images/bitmap.js
Normal file
@ -0,0 +1,173 @@
|
||||
wdi.BMP2 = $.spcExtend(wdi.SpiceObject, {
|
||||
objectSize: 0,
|
||||
mapper: [0, 1, 1, 4, 4, 8, 16, 24, 32, 32],
|
||||
|
||||
init: function(imageData) {
|
||||
var type = this.bytesToInt8(imageData);
|
||||
var flags = this.bytesToInt8(imageData); // bit 1 => normal, bit 2 => palette cache (...)
|
||||
var width = this.bytesToInt32(imageData); // width in pixels
|
||||
var height = this.bytesToInt32(imageData); // height in pixels
|
||||
var stride = this.bytesToInt32(imageData); // width in bytes including padding
|
||||
var len;
|
||||
var bpp = this.mapper[type];
|
||||
var i;
|
||||
|
||||
|
||||
var paletteSize = 0, unique, paletteData, numEnts = 0;
|
||||
if (bpp <= 8 && bpp > 0) {
|
||||
var palette = [];
|
||||
if (flags & 1) {
|
||||
var paletteOffset = this.bytesToInt32(imageData); // From the begininig of the spice packet?
|
||||
len = imageData.length;
|
||||
paletteSize = 4*Math.pow(2,bpp);
|
||||
var paletteDataSize = paletteSize + 8 + 2; //palette + unique(64b) + numEnts (16b)
|
||||
len -= paletteDataSize;
|
||||
paletteData = imageData.splice(len, paletteDataSize);
|
||||
unique = this.bytesToInt64(paletteData);
|
||||
numEnts = this.bytesToInt16(paletteData);
|
||||
var queue;
|
||||
|
||||
for (i = 0; i < numEnts*4; i+=4) {
|
||||
queue = new wdi.Queue();
|
||||
queue.setData(paletteData.slice(i, i+4));
|
||||
palette.push(new wdi.SpiceColor().demarshall(queue));
|
||||
}
|
||||
wdi.ImageCache.addPalette(unique, palette);
|
||||
} else {
|
||||
//get palette from cache
|
||||
unique = this.bytesToInt64(imageData);
|
||||
len = imageData.length;
|
||||
palette = wdi.ImageCache.getPalette(unique);
|
||||
var spiceColors;
|
||||
paletteData = [];
|
||||
numEnts = palette.length;
|
||||
for (i =0; i < numEnts; i++ ) {
|
||||
spiceColors = palette[i].marshall();
|
||||
spiceColors.push(0);
|
||||
paletteData = paletteData.concat(spiceColors);
|
||||
}
|
||||
|
||||
}
|
||||
// imageData = paletteData.concat(imageData);
|
||||
|
||||
} else {
|
||||
// Removing 4 bytes from the image data to fix index out of range error.
|
||||
var unknown = this.bytesToInt32(imageData);
|
||||
}
|
||||
|
||||
this.setContent({
|
||||
imageSize: len,
|
||||
width: width,
|
||||
height: height,
|
||||
bpp: bpp,
|
||||
imageData: imageData,
|
||||
paletteSize: numEnts * 4,
|
||||
palette: palette,
|
||||
stride: stride,
|
||||
type: type
|
||||
});
|
||||
},
|
||||
|
||||
setContent: function(c) {
|
||||
this.imageSize = c.imageSize;
|
||||
this.width = c.width;
|
||||
this.height = c.height;
|
||||
this.bpp = c.bpp;
|
||||
this.imageData = c.imageData;
|
||||
this.palette = c.palette;
|
||||
this.offset = c.paletteSize + 0x36; //0x36 === Current harcoded header size (BMP + DIB)
|
||||
this.size = this.offset + this.imageSize;
|
||||
this.stride = c.stride;
|
||||
this.type = c.type;
|
||||
},
|
||||
|
||||
marshall: function(context) {
|
||||
var type = this.type;
|
||||
var palette = this.palette;
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
var stride = this.stride;
|
||||
var data = this.imageData;
|
||||
var size = data.length;
|
||||
|
||||
var pixelsStride = stride * 8/this.bpp;
|
||||
var bytesStride = pixelsStride * 4;
|
||||
var buf = new ArrayBuffer(bytesStride * height);
|
||||
var buf8 = new Uint8ClampedArray(buf);
|
||||
var buf32 = new Uint32Array(buf);
|
||||
var topdown = false;
|
||||
|
||||
var oct, i, pos, buffPos, spiceColor;
|
||||
var b;
|
||||
if (palette) {
|
||||
buffPos = 0
|
||||
if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_1BIT_BE) {
|
||||
spiceColor = palette[1];
|
||||
var foreColor = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
|
||||
|
||||
spiceColor = palette[0];
|
||||
var backColor = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
|
||||
|
||||
var PLT1_MASK = [1, 2, 4, 8, 16, 32, 64, 128];
|
||||
|
||||
for (pos = 0; pos < size; pos++) {
|
||||
oct = data[pos];
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
if (oct & PLT1_MASK[i]) {
|
||||
buf32[buffPos++] = foreColor;
|
||||
} else {
|
||||
buf32[buffPos++] = backColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_4BIT_BE) {
|
||||
for (pos = 0; pos < size; pos++) {
|
||||
oct = data[pos];
|
||||
spiceColor = palette[oct >>> 4];
|
||||
buf32[buffPos++] = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
|
||||
spiceColor = palette[oct & 0x0f];
|
||||
buf32[buffPos++] = spiceColor.r << 24 | spiceColor.g << 16 | spiceColor.b << 8 | 255;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_32BIT) {
|
||||
for (pos = 0; pos < size; pos += 4) {
|
||||
b = data[pos];
|
||||
data[pos] = data[pos + 2];
|
||||
data[pos + 2] = b;
|
||||
data[pos + 3] = 255;
|
||||
}
|
||||
|
||||
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_RGBA) {
|
||||
topdown = true;
|
||||
for (pos = 0; pos < size; pos+=4) {
|
||||
b = data[pos];
|
||||
data[pos] = data[pos+2];
|
||||
data[pos+2] = b;
|
||||
}
|
||||
} else if (type === wdi.SpiceBitmapFmt.SPICE_BITMAP_FMT_24BIT) {
|
||||
for (pos = 0; pos < size; pos+=3) {
|
||||
b = data[pos];
|
||||
data[pos] = data[pos+2];
|
||||
data[pos+2] = b;
|
||||
}
|
||||
}
|
||||
buf8 = new Uint8ClampedArray(data);
|
||||
}
|
||||
|
||||
var ret = new ImageData(buf8, pixelsStride, height);
|
||||
|
||||
var tmpCanvas = wdi.graphics.getNewTmpCanvas(width, height);
|
||||
tmpCanvas.getContext('2d').putImageData(ret, 0, 0, 0, 0, width, height);
|
||||
ret = tmpCanvas;
|
||||
|
||||
if(!topdown) {
|
||||
ret = wdi.RasterOperation.flip(ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
});
|
65
js/spice-web-client/lib/images/jsquic_family.js
Normal file
65
js/spice-web-client/lib/images/jsquic_family.js
Normal file
@ -0,0 +1,65 @@
|
||||
function golomb_code_len(n, l) {
|
||||
if (n < wdi.nGRcodewords[l]) {
|
||||
return (n >>> l) + 1 + l;
|
||||
} else {
|
||||
return wdi.notGRcwlen[l];
|
||||
}
|
||||
}
|
||||
|
||||
function golomb_decoding(l, bits, bppmask) {
|
||||
var cwlen;
|
||||
var result;
|
||||
if (bits > wdi.notGRprefixmask[l]) {
|
||||
var zeroprefix = cnt_l_zeroes(bits);
|
||||
cwlen = zeroprefix + 1 + l;
|
||||
result = ( (zeroprefix << l) >>> 0) | ((bits >>> (32 - cwlen)) & bppmask[l]);
|
||||
} else {
|
||||
cwlen = wdi.notGRcwlen[l];
|
||||
result = wdi.nGRcodewords[l] + ((bits) >>> (32 - cwlen) & bppmask[wdi.notGRsuffixlen[l]]);
|
||||
}
|
||||
return [result,cwlen];
|
||||
}
|
||||
|
||||
/* update the bucket using just encoded curval */
|
||||
function real_update_model(state, bucket, curval, bpp) {
|
||||
var i;
|
||||
var bestcode;
|
||||
var bestcodelen;
|
||||
var ithcodelen;
|
||||
|
||||
var pcounters = bucket.pcounters;
|
||||
bestcode = bpp - 1;
|
||||
bestcodelen = (pcounters[bestcode] += golomb_code_len(curval, bestcode));
|
||||
|
||||
for (i = bpp - 2; i >= 0; i--) {
|
||||
ithcodelen = (pcounters[i] += golomb_code_len(curval, i));
|
||||
|
||||
if (ithcodelen < bestcodelen) {
|
||||
bestcode = i;
|
||||
bestcodelen = ithcodelen;
|
||||
}
|
||||
}
|
||||
|
||||
bucket.bestcode = bestcode;
|
||||
if (bestcodelen > state.wm_trigger) {
|
||||
for (i = 0; i < bpp; i++) {
|
||||
pcounters[i] >>>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function UPDATE_MODEL(index, encoder, bpp, correlate_row_r, correlate_row_g, correlate_row_b) {
|
||||
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[0],
|
||||
correlate_row_r[index - 1]), correlate_row_r[index], bpp);
|
||||
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[1],
|
||||
correlate_row_g[index - 1]), correlate_row_g[index], bpp);
|
||||
real_update_model(encoder.rgb_state, find_bucket(encoder.channels[2],
|
||||
correlate_row_b[index - 1]), correlate_row_b[index], bpp);
|
||||
}
|
||||
|
||||
function find_bucket(channel, val) {
|
||||
if(val===undefined) {
|
||||
val=channel.oldFirst;
|
||||
}
|
||||
return channel._buckets_ptrs[val];
|
||||
}
|
324
js/spice-web-client/lib/images/jsquic_rgba.js
Normal file
324
js/spice-web-client/lib/images/jsquic_rgba.js
Normal file
@ -0,0 +1,324 @@
|
||||
function QUIC_UNCOMPRESS_RGBA(encoder, channels, bpc, type) {
|
||||
quic_uncompress_row0(encoder, channels, bpc, type);
|
||||
quic_four_uncompress_row0(encoder, encoder.channels[3], bpc, type);
|
||||
encoder.row_completed();
|
||||
var height = encoder.height;
|
||||
var rgb_state = encoder.rgb_state;
|
||||
for (var row = 1; row < height; row++) {
|
||||
quic_uncompress_row(encoder, channels, bpc, type, rgb_state);
|
||||
quic_four_uncompress_row(encoder, encoder.channels[3], bpc, type);
|
||||
encoder.row_completed();
|
||||
}
|
||||
}
|
||||
|
||||
function quic_four_uncompress_row(encoder, channel, bpc, type) {
|
||||
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
|
||||
var pos = 0;
|
||||
var width = encoder.width;
|
||||
while ((wdi.JSQuic.wmimax > channel.state.wmidx) && (channel.state.wmileft <= width)) {
|
||||
if (channel.state.wmileft) {
|
||||
uncompress_four_row_seg(
|
||||
encoder,
|
||||
channel,
|
||||
pos,
|
||||
pos + channel.state.wmileft,
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
pos += channel.state.wmileft;
|
||||
width -= channel.state.wmileft;
|
||||
}
|
||||
|
||||
channel.state.wmidx++;
|
||||
wdi.JSQuic.set_wm_trigger(channel.state);
|
||||
channel.state.wmileft = wdi.JSQuic.wminext;
|
||||
}
|
||||
|
||||
if (width) {
|
||||
uncompress_four_row_seg(
|
||||
encoder,
|
||||
channel,
|
||||
pos,
|
||||
pos + width,
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
if (wdi.JSQuic.wmimax > channel.state.wmidx) {
|
||||
channel.state.wmileft -= width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uncompress_four_row_seg(encoder, channel, i, end, bpc, bpc_mask, type) {
|
||||
var correlate_row = channel.correlate_row;
|
||||
|
||||
var stopidx = 0;
|
||||
|
||||
var waitmask = wdi.JSQuic.bppmask[channel.state.wmidx];
|
||||
|
||||
var run_index = 0;
|
||||
var run_end = 0;
|
||||
|
||||
var rle = false;
|
||||
|
||||
var computedWidth = encoder.computedWidth;
|
||||
|
||||
var rows_completed = encoder.rows_completed;
|
||||
|
||||
var offset = ((encoder.rows_completed-1) * computedWidth);
|
||||
|
||||
var data;
|
||||
|
||||
var eatbits = encoder.eatbits;
|
||||
var appendAlpha = encoder.appendAlpha;
|
||||
|
||||
var alpha;
|
||||
|
||||
var ret, codewordlen;
|
||||
var bppmask = wdi.JSQuic.bppmask;
|
||||
|
||||
if (!i) {
|
||||
alpha = UNCOMPRESS_ONE_0_A(channel, encoder, bpc_mask, offset);
|
||||
|
||||
if (channel.state.waitcnt) {
|
||||
--channel.state.waitcnt;
|
||||
} else {
|
||||
channel.state.waitcnt = (wdi.JSQuic.tabrand(channel.state) & waitmask);
|
||||
real_update_model(channel.state, find_bucket(channel,
|
||||
correlate_row[-1]), correlate_row[0], bpc);
|
||||
}
|
||||
stopidx = ++i + channel.state.waitcnt;
|
||||
} else {
|
||||
stopidx = i + channel.state.waitcnt;
|
||||
alpha = encoder.result[encoder.alphaPos-4];
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
while (stopidx < end) {
|
||||
for (; i <= stopidx; i++) {
|
||||
rle = RLE_PRED_A(i, encoder, run_index, computedWidth, rows_completed);
|
||||
if(rle) break;
|
||||
|
||||
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
|
||||
encoder.io_word, bppmask);
|
||||
|
||||
data = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row[i] = data;
|
||||
alpha = (((wdi.xlatL2U[data] +
|
||||
((alpha + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0);
|
||||
|
||||
appendAlpha.call(encoder, alpha);
|
||||
eatbits.call(encoder, codewordlen);
|
||||
}
|
||||
if(!rle) {
|
||||
real_update_model(channel.state, find_bucket(channel,
|
||||
correlate_row[stopidx-1]), correlate_row[stopidx], bpc);
|
||||
stopidx = i + (wdi.JSQuic.tabrand(channel.state) & waitmask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!rle) {
|
||||
for (; i < end; i++) {
|
||||
rle = RLE_PRED_A(i, encoder, run_index, computedWidth, rows_completed);
|
||||
if(rle) break;
|
||||
|
||||
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
|
||||
encoder.io_word, bppmask);
|
||||
|
||||
data = ret[0];
|
||||
codewordlen = ret[1];
|
||||
correlate_row[i] = data;
|
||||
alpha = (((wdi.xlatL2U[data] +
|
||||
((alpha + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0);
|
||||
|
||||
appendAlpha.call(encoder, alpha);
|
||||
eatbits.call(encoder, codewordlen);
|
||||
}
|
||||
if(!rle) {
|
||||
channel.state.waitcnt = stopidx - end;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//RLE
|
||||
channel.state.waitcnt = stopidx - i;
|
||||
run_index = i;
|
||||
run_end = i + wdi.JSQuic.decode_channel_run(encoder, channel);
|
||||
|
||||
var cpos = ((encoder.rows_completed) * (encoder.width*4)) + (i*4);
|
||||
var a = encoder.result[cpos-1];
|
||||
|
||||
for (; i < run_end; i++) {
|
||||
//TODO: how to append?
|
||||
appendAlpha.call(encoder, a);
|
||||
}
|
||||
|
||||
if (i === end) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopidx = i + channel.state.waitcnt;
|
||||
rle = false;
|
||||
//END RLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function quic_four_uncompress_row0(encoder, channel, bpc, type) {
|
||||
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
|
||||
var pos = 0;
|
||||
var width = encoder.width;
|
||||
while ((wdi.JSQuic.wmimax > channel.state.wmidx) && (channel.state.wmileft <= width)) {
|
||||
if (channel.state.wmileft) {
|
||||
uncompress_four_row0_seg(
|
||||
encoder,
|
||||
channel,
|
||||
pos,
|
||||
pos + channel.wmileft,
|
||||
wdi.JSQuic.bppmask[channel.state.wmidx],
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
pos += channel.state.wmileft;
|
||||
width -= channel.state.wmileft;
|
||||
}
|
||||
|
||||
channel.state.wmidx++;
|
||||
wdi.JSQuic.set_wm_trigger(channel.state);
|
||||
channel.state.wmileft = wdi.JSQuic.wminext;
|
||||
}
|
||||
|
||||
if (width) {
|
||||
uncompress_four_row0_seg(
|
||||
encoder,
|
||||
channel,
|
||||
pos,
|
||||
pos + width,
|
||||
wdi.JSQuic.bppmask[channel.state.wmidx],
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
if (wdi.JSQuic.wmimax > channel.state.wmidx) {
|
||||
channel.state.wmileft -= width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uncompress_four_row0_seg(encoder, channel, i, end, waitmask, bpc, bpc_mask, type) {
|
||||
var correlate_row = channel.correlate_row;
|
||||
|
||||
var stopidx = 0;
|
||||
|
||||
if (!i) {
|
||||
UNCOMPRESS_ONE_ROW0_0_A(channel);
|
||||
|
||||
if (channel.state.waitcnt) {
|
||||
--channel.state.waitcnt;
|
||||
} else {
|
||||
channel.state.waitcnt = (wdi.JSQuic.tabrand(channel.state) & waitmask);
|
||||
real_update_model(channel.state, find_bucket(channel,
|
||||
correlate_row[-1]), correlate_row[0], bpc);
|
||||
}
|
||||
stopidx = ++i + channel.state.waitcnt;
|
||||
} else {
|
||||
stopidx = i + channel.state.waitcnt;
|
||||
}
|
||||
|
||||
while (stopidx < end) {
|
||||
for (; i <= stopidx; i++) {
|
||||
UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row);
|
||||
}
|
||||
real_update_model(channel.state, find_bucket(channel,
|
||||
correlate_row[stopidx-1]), correlate_row[stopidx], bpc);
|
||||
stopidx = i + (wdi.JSQuic.tabrand(channel.state) & waitmask);
|
||||
}
|
||||
|
||||
for (; i < end; i++) {
|
||||
UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row);
|
||||
}
|
||||
channel.state.waitcnt = stopidx - end;
|
||||
}
|
||||
|
||||
function SAME_PIXEL_A(i, result) {
|
||||
if(result[i-1] === result[i+3]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function RLE_PRED_A(i, encoder, run_index, width, rows_completed) {
|
||||
var pr = ((rows_completed-1) * width) + (i*4); //prev r
|
||||
if(run_index !== i && i > 2) {
|
||||
if(SAME_PIXEL_A(pr, encoder.result)) {
|
||||
pr = ((rows_completed) * width) + ((i-1)*4); // cur r
|
||||
if(SAME_PIXEL_A(pr, encoder.result)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE_0_A(channel, encoder, bpc_mask, offset) {
|
||||
var ret, codewordlen;
|
||||
channel.oldFirst = channel.correlate_row[0];
|
||||
ret = golomb_decoding(find_bucket(channel,
|
||||
channel.correlate_row[-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
channel.correlate_row[0] = ret[0];
|
||||
codewordlen = ret[1];
|
||||
var residuum = wdi.xlatL2U[channel.correlate_row[0]];
|
||||
var prev = PIXEL_B(channel, encoder, 0, offset);
|
||||
var resultpixel = ((residuum + prev) & bpc_mask) >>> 0;
|
||||
encoder.appendAlpha(resultpixel);
|
||||
encoder.eatbits(codewordlen);
|
||||
return resultpixel;
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE_A(channel, i, bpc_mask, encoder, correlate_row, offset) {
|
||||
var ret, codewordlen;
|
||||
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode,
|
||||
encoder.io_word, wdi.JSQuic.bppmask);
|
||||
var data = ret[0];
|
||||
codewordlen = ret[1];
|
||||
correlate_row[i] = data;
|
||||
encoder.appendAlpha((((wdi.xlatL2U[data] +
|
||||
((PIXEL_A_A(encoder) + PIXEL_B(channel, encoder, i, offset)) >>> 1)) & bpc_mask) >>> 0));
|
||||
|
||||
encoder.eatbits(codewordlen);
|
||||
}
|
||||
|
||||
|
||||
function UNCOMPRESS_ONE_ROW0_0_A(channel) {
|
||||
var ret, codewordlen;
|
||||
var encoder = channel.encoder;
|
||||
ret = golomb_decoding(find_bucket(channel, 0).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
channel.correlate_row[0] = ret[0];
|
||||
codewordlen = ret[1];
|
||||
encoder.appendAlpha(wdi.xlatL2U[channel.correlate_row[0]]);
|
||||
encoder.eatbits(codewordlen);
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE_ROW0_A(channel, i, bpc_mask, encoder, correlate_row) {
|
||||
var ret, codewordlen;
|
||||
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
var data = ret[0];
|
||||
codewordlen = ret[1];
|
||||
correlate_row[i] = data;
|
||||
encoder.appendAlpha(CORELATE_0_A(encoder, channel, i, bpc_mask, correlate_row));
|
||||
encoder.eatbits(codewordlen);
|
||||
}
|
||||
|
||||
function CORELATE_0_A(encoder, channel, curr, bpc_mask, correlate_row) {
|
||||
return ((wdi.xlatL2U[correlate_row[curr]] + PIXEL_A_A(encoder)) & bpc_mask) >>> 0;
|
||||
}
|
||||
|
||||
function PIXEL_A_A(encoder) {
|
||||
return encoder.result[encoder.alphaPos - 4];
|
||||
}
|
511
js/spice-web-client/lib/images/jsquic_uncompress.js
Normal file
511
js/spice-web-client/lib/images/jsquic_uncompress.js
Normal file
@ -0,0 +1,511 @@
|
||||
function QUIC_UNCOMPRESS_RGB(encoder, channels, bpc, type) {
|
||||
quic_uncompress_row0(encoder, channels, bpc, type);
|
||||
encoder.row_completed();
|
||||
var rgb_state = encoder.rgb_state;
|
||||
var height = encoder.height;
|
||||
for (var row = 1; row < height; row++) {
|
||||
quic_uncompress_row(encoder, channels, bpc, type, rgb_state);
|
||||
encoder.row_completed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function quic_uncompress_row(encoder, channels, bpc, type, rgb_state) {
|
||||
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
|
||||
var pos = 0;
|
||||
var width = encoder.width;
|
||||
while ((wdi.JSQuic.wmimax > rgb_state.wmidx) && (rgb_state.wmileft <= width)) {
|
||||
if (rgb_state.wmileft) {
|
||||
uncompress_row_seg(
|
||||
encoder,
|
||||
pos,
|
||||
pos + rgb_state.wmileft,
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type,
|
||||
rgb_state
|
||||
);
|
||||
pos += rgb_state.wmileft;
|
||||
width -= rgb_state.wmileft;
|
||||
}
|
||||
|
||||
rgb_state.wmidx++;
|
||||
wdi.JSQuic.set_wm_trigger(rgb_state);
|
||||
rgb_state.wmileft = wdi.JSQuic.wminext;
|
||||
}
|
||||
|
||||
if (width) {
|
||||
uncompress_row_seg(
|
||||
encoder,
|
||||
pos,
|
||||
pos + width,
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type,
|
||||
rgb_state
|
||||
);
|
||||
if (wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) {
|
||||
rgb_state.wmileft -= width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SAME_PIXEL(i, result) {
|
||||
if(result[i-4] === result[i] && result[i-3] === result[i+1] &&
|
||||
result[i-2] === result[i+2]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function RLE_PRED(i, encoder, offset, currentOffset, run_index) {
|
||||
if(run_index !== i && i > 2) {
|
||||
if(SAME_PIXEL(offset, encoder.result)) { //fila de arriba
|
||||
var pr = currentOffset + ((i-1)*4);
|
||||
if(SAME_PIXEL(pr, encoder.result)) { //pixel de la izquierda
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function uncompress_row_seg(encoder, i, end, bpc, bpc_mask, type, rgb_state) {
|
||||
var channel_r = encoder.channels[0];
|
||||
var channel_g = encoder.channels[1];
|
||||
var channel_b = encoder.channels[2];
|
||||
|
||||
var buckets_ptrs_r = channel_r._buckets_ptrs;
|
||||
var buckets_ptrs_g = channel_g._buckets_ptrs;
|
||||
var buckets_ptrs_b = channel_b._buckets_ptrs;
|
||||
|
||||
var correlate_row_r = channel_r.correlate_row;
|
||||
var correlate_row_g = channel_g.correlate_row;
|
||||
var correlate_row_b = channel_b.correlate_row;
|
||||
|
||||
var stopidx = 0;
|
||||
|
||||
var waitmask = wdi.JSQuic.bppmask[rgb_state.wmidx];
|
||||
|
||||
var run_index = 0;
|
||||
var run_end = 0;
|
||||
|
||||
var rle = false;
|
||||
|
||||
var computedWidth = encoder.computedWidth;
|
||||
|
||||
var offset = ((encoder.rows_completed-1) * computedWidth);
|
||||
var currentOffset = ((encoder.rows_completed) * computedWidth);
|
||||
|
||||
var result = encoder.result;
|
||||
var resultData = encoder.resultData;
|
||||
|
||||
var i_1,i_4; //for performance improvments
|
||||
|
||||
var xlatL2U = wdi.xlatL2U;
|
||||
|
||||
var pr, pg, pb;
|
||||
|
||||
var prevCorrelatedR, prevCorrelatedG, prevCorrelatedB;
|
||||
|
||||
var eatbits = encoder.eatbits;
|
||||
var tabrand = wdi.JSQuic.tabrand;
|
||||
var decode_run = wdi.JSQuic.decode_run;
|
||||
|
||||
var ret, codewordlen;
|
||||
|
||||
var cnt = encoder.cnt;
|
||||
var pxCnt = encoder.pxCnt;
|
||||
|
||||
var bppmask = wdi.JSQuic.bppmask;
|
||||
|
||||
if (!i) {
|
||||
pr = UNCOMPRESS_ONE_0(channel_r, encoder, bpc_mask, offset);
|
||||
pg = UNCOMPRESS_ONE_0(channel_g, encoder, bpc_mask, offset);
|
||||
pb = UNCOMPRESS_ONE_0(channel_b, encoder, bpc_mask, offset);
|
||||
|
||||
prevCorrelatedR = correlate_row_r[0];
|
||||
prevCorrelatedG = correlate_row_g[0];
|
||||
prevCorrelatedB = correlate_row_b[0];
|
||||
//inlined appendPixel
|
||||
resultData[pxCnt++] =
|
||||
(255 << 24) | // alpha
|
||||
(pb << 16) | // blue
|
||||
(pg << 8) | // green
|
||||
pr; // red
|
||||
cnt += 4;
|
||||
|
||||
if (rgb_state.waitcnt) {
|
||||
--rgb_state.waitcnt;
|
||||
} else {
|
||||
rgb_state.waitcnt = (tabrand.call(wdi.JSQuic, rgb_state) & waitmask);
|
||||
UPDATE_MODEL(0, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
|
||||
}
|
||||
stopidx = ++i + rgb_state.waitcnt;
|
||||
} else {
|
||||
stopidx = i + rgb_state.waitcnt;
|
||||
pr = result[cnt-4];
|
||||
pg = result[cnt-3];
|
||||
pb = result[cnt-2];
|
||||
|
||||
prevCorrelatedR = correlate_row_r[i-1];
|
||||
prevCorrelatedG = correlate_row_g[i-1];
|
||||
prevCorrelatedB = correlate_row_b[i-1];
|
||||
}
|
||||
|
||||
while(true) {
|
||||
while (stopidx < end) {
|
||||
i_4 = offset + i*4;
|
||||
for (; i <= stopidx; i++) {
|
||||
|
||||
rle = RLE_PRED(i, encoder, i_4, currentOffset, run_index);
|
||||
|
||||
if(rle) break;
|
||||
|
||||
i_1 = i-1;
|
||||
|
||||
/////////////////////// INLINING UNCOMPRESS_ONE
|
||||
//r
|
||||
ret = golomb_decoding(buckets_ptrs_r[prevCorrelatedR].bestcode,
|
||||
encoder.io_word, bppmask);
|
||||
|
||||
prevCorrelatedR = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row_r[i] = prevCorrelatedR;
|
||||
|
||||
pr = ((((xlatL2U[prevCorrelatedR] +
|
||||
((pr +
|
||||
result[i_4]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
|
||||
//g
|
||||
ret = golomb_decoding(buckets_ptrs_g[prevCorrelatedG].bestcode,
|
||||
encoder.io_word, bppmask);
|
||||
|
||||
prevCorrelatedG = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row_g[i] = prevCorrelatedG;
|
||||
|
||||
pg = ((((xlatL2U[prevCorrelatedG] +
|
||||
((pg +
|
||||
result[i_4 + 1]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
|
||||
//b
|
||||
ret = golomb_decoding(buckets_ptrs_b[prevCorrelatedB].bestcode,
|
||||
encoder.io_word, bppmask);
|
||||
|
||||
prevCorrelatedB = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row_b[i] = prevCorrelatedB;
|
||||
|
||||
pb = ((((xlatL2U[prevCorrelatedB] +
|
||||
((pb +
|
||||
result[i_4 + 2]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
////////////////////// END INLINING
|
||||
|
||||
/**
|
||||
pr = UNCOMPRESS_ONE(channel_r, i, i_1, i_4, bpc_mask, encoder, correlate_row_r, offset, result, 0, xlatL2U, buckets_ptrs_r, pr);
|
||||
pg = UNCOMPRESS_ONE(channel_g, i, i_1, i_4, bpc_mask, encoder, correlate_row_g, offset, result, 1, xlatL2U, buckets_ptrs_g, pg);
|
||||
pb = UNCOMPRESS_ONE(channel_b, i, i_1, i_4, bpc_mask, encoder, correlate_row_b, offset, result, 2, xlatL2U, buckets_ptrs_b, pb);
|
||||
**/
|
||||
|
||||
|
||||
//this is inlined appendPixel
|
||||
resultData[pxCnt++] =
|
||||
(255 << 24) | // alpha
|
||||
(pb << 16) | // blue
|
||||
(pg << 8) | // green
|
||||
pr; // red
|
||||
cnt += 4;
|
||||
i_4 += 4;
|
||||
}
|
||||
if(!rle) {
|
||||
UPDATE_MODEL(stopidx, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
|
||||
stopidx = i + (tabrand.call(wdi.JSQuic, rgb_state) & waitmask);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!rle) {
|
||||
i_4 = offset + i*4;
|
||||
for (; i < end; i++) {
|
||||
rle = RLE_PRED(i, encoder, i_4, currentOffset, run_index);
|
||||
if(rle) break;
|
||||
|
||||
i_1 = i-1;
|
||||
|
||||
|
||||
////////////////////// INLINING UNCOMPRESS_ONE
|
||||
//r
|
||||
ret = golomb_decoding(buckets_ptrs_r[prevCorrelatedR].bestcode, encoder.io_word, bppmask);
|
||||
prevCorrelatedR = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row_r[i] = prevCorrelatedR;
|
||||
|
||||
pr = ((((xlatL2U[prevCorrelatedR] +
|
||||
((pr +
|
||||
result[i_4]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
|
||||
//g
|
||||
ret = golomb_decoding(buckets_ptrs_g[prevCorrelatedG].bestcode, encoder.io_word, bppmask);
|
||||
prevCorrelatedG = ret[0];
|
||||
codewordlen = ret[1];
|
||||
correlate_row_g[i] = prevCorrelatedG;
|
||||
|
||||
pg = ((((xlatL2U[prevCorrelatedG] +
|
||||
((pg +
|
||||
result[i_4 + 1]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
|
||||
//b
|
||||
ret = golomb_decoding(buckets_ptrs_b[prevCorrelatedB].bestcode, encoder.io_word, bppmask);
|
||||
prevCorrelatedB = ret[0];
|
||||
codewordlen = ret[1];
|
||||
|
||||
correlate_row_b[i] = prevCorrelatedB;
|
||||
|
||||
pb = ((((xlatL2U[prevCorrelatedB] +
|
||||
((pb +
|
||||
result[i_4 + 2]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
eatbits.call(encoder, codewordlen);
|
||||
///////////////////// END INLINING
|
||||
|
||||
/**
|
||||
pr = UNCOMPRESS_ONE(channel_r, i, i_1, i_4, bpc_mask, encoder, correlate_row_r, offset, result, 0, xlatL2U, buckets_ptrs_r, pr);
|
||||
pg = UNCOMPRESS_ONE(channel_g, i, i_1, i_4, bpc_mask, encoder, correlate_row_g, offset, result, 1, xlatL2U, buckets_ptrs_g, pg);
|
||||
pb = UNCOMPRESS_ONE(channel_b, i, i_1, i_4, bpc_mask, encoder, correlate_row_b, offset, result, 2, xlatL2U, buckets_ptrs_b, pb);
|
||||
**/
|
||||
|
||||
//this is inlined apendPixel
|
||||
resultData[pxCnt++] =
|
||||
(255 << 24) | // alpha
|
||||
(pb << 16) | // blue
|
||||
(pg << 8) | // green
|
||||
pr; // red
|
||||
cnt += 4;
|
||||
i_4 += 4;
|
||||
}
|
||||
if(!rle) {
|
||||
rgb_state.waitcnt = stopidx - end;
|
||||
encoder.cnt = cnt;
|
||||
encoder.pxCnt = pxCnt;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//RLE
|
||||
rgb_state.waitcnt = stopidx - i;
|
||||
run_index = i;
|
||||
run_end = i + decode_run.call(wdi.JSQuic, encoder);
|
||||
|
||||
for (; i < run_end; i++) {
|
||||
//this is inlined appendPixel
|
||||
resultData[pxCnt++] =
|
||||
(255 << 24) | // alpha
|
||||
(pb << 16) | // blue
|
||||
(pg << 8) | // green
|
||||
pr; // red
|
||||
cnt += 4;
|
||||
}
|
||||
|
||||
if (i === end) {
|
||||
encoder.cnt = cnt;
|
||||
encoder.pxCnt = pxCnt;
|
||||
return;
|
||||
}
|
||||
|
||||
i_1 = i-1;
|
||||
prevCorrelatedR = correlate_row_r[i_1];
|
||||
prevCorrelatedG = correlate_row_g[i_1];
|
||||
prevCorrelatedB = correlate_row_b[i_1];
|
||||
|
||||
stopidx = i + rgb_state.waitcnt;
|
||||
rle = false;
|
||||
//END RLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function quic_uncompress_row0(encoder, channels, bpc, type) {
|
||||
var bpc_mask = wdi.JSQuic.BPC_MASK[type];
|
||||
var pos = 0;
|
||||
var width = encoder.width;
|
||||
while ((wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) && (encoder.rgb_state.wmileft <= width)) {
|
||||
if (encoder.rgb_state.wmileft) {
|
||||
uncompress_row0_seg(
|
||||
encoder,
|
||||
pos,
|
||||
pos + encoder.rgb_state.wmileft,
|
||||
wdi.JSQuic.bppmask[encoder.rgb_state.wmidx],
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
pos += encoder.rgb_state.wmileft;
|
||||
width -= encoder.rgb_state.wmileft;
|
||||
}
|
||||
|
||||
encoder.rgb_state.wmidx++;
|
||||
wdi.JSQuic.set_wm_trigger(encoder.rgb_state);
|
||||
encoder.rgb_state.wmileft = wdi.JSQuic.wminext;
|
||||
}
|
||||
|
||||
if (width) {
|
||||
uncompress_row0_seg(
|
||||
encoder,
|
||||
pos,
|
||||
pos + width,
|
||||
wdi.JSQuic.bppmask[encoder.rgb_state.wmidx],
|
||||
bpc,
|
||||
bpc_mask,
|
||||
type
|
||||
);
|
||||
if (wdi.JSQuic.wmimax > encoder.rgb_state.wmidx) {
|
||||
encoder.rgb_state.wmileft -= width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uncompress_row0_seg(encoder, i, end, waitmask, bpc, bpc_mask, type) {
|
||||
var channel_r = encoder.channels[0];
|
||||
var channel_g = encoder.channels[1];
|
||||
var channel_b = encoder.channels[2];
|
||||
|
||||
var correlate_row_r = channel_r.correlate_row;
|
||||
var correlate_row_g = channel_g.correlate_row;
|
||||
var correlate_row_b = channel_b.correlate_row;
|
||||
|
||||
var stopidx = 0;
|
||||
|
||||
var pr, pg, pb;
|
||||
|
||||
if (!i) {
|
||||
pr = UNCOMPRESS_ONE_ROW0_0(channel_r);
|
||||
pg = UNCOMPRESS_ONE_ROW0_0(channel_g);
|
||||
pb = UNCOMPRESS_ONE_ROW0_0(channel_b);
|
||||
|
||||
encoder.appendPixel(pr, pg, pb);
|
||||
|
||||
if (encoder.rgb_state.waitcnt) {
|
||||
--encoder.rgb_state.waitcnt;
|
||||
} else {
|
||||
encoder.rgb_state.waitcnt = (wdi.JSQuic.tabrand(encoder.rgb_state) & waitmask);
|
||||
UPDATE_MODEL(0, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
|
||||
}
|
||||
stopidx = ++i + encoder.rgb_state.waitcnt;
|
||||
} else {
|
||||
stopidx = i + encoder.rgb_state.waitcnt;
|
||||
}
|
||||
|
||||
while (stopidx < end) {
|
||||
for (; i <= stopidx; i++) {
|
||||
pr = UNCOMPRESS_ONE_ROW0(channel_r, i, bpc_mask, encoder, correlate_row_r, pr);
|
||||
pg = UNCOMPRESS_ONE_ROW0(channel_g, i, bpc_mask, encoder, correlate_row_g, pg);
|
||||
pb = UNCOMPRESS_ONE_ROW0(channel_b, i, bpc_mask, encoder, correlate_row_b, pb);
|
||||
|
||||
encoder.appendPixel(pr, pg, pb);
|
||||
}
|
||||
UPDATE_MODEL(stopidx, encoder, bpc, correlate_row_r, correlate_row_g, correlate_row_b);
|
||||
stopidx = i + (wdi.JSQuic.tabrand(encoder.rgb_state) & waitmask);
|
||||
}
|
||||
|
||||
for (; i < end; i++) {
|
||||
pr = UNCOMPRESS_ONE_ROW0(channel_r, i, bpc_mask, encoder, correlate_row_r, pr);
|
||||
pg = UNCOMPRESS_ONE_ROW0(channel_g, i, bpc_mask, encoder, correlate_row_g, pg);
|
||||
pb =UNCOMPRESS_ONE_ROW0(channel_b, i, bpc_mask, encoder, correlate_row_b, pb);
|
||||
encoder.appendPixel(pr, pg, pb);
|
||||
}
|
||||
encoder.rgb_state.waitcnt = stopidx - end;
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE_0(channel, encoder, bpc_mask, offset) {
|
||||
var ret, codewordlen;
|
||||
channel.oldFirst = channel.correlate_row[0];
|
||||
ret = golomb_decoding(find_bucket(channel,
|
||||
channel.correlate_row[0]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
channel.correlate_row[0] = ret[0];
|
||||
codewordlen = ret[1];
|
||||
var residuum = wdi.xlatL2U[channel.correlate_row[0]];
|
||||
var prev = encoder.result[offset + channel.num_channel]; //PIXEL_B
|
||||
var resultpixel = ((residuum + prev) & bpc_mask);
|
||||
encoder.eatbits(codewordlen);
|
||||
return resultpixel;
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE(channel, i, i_1, i_4, bpc_mask, encoder, correlate_row, offset, result, num_channel, xlatL2U, buckets_ptrs, prev_pixel) {
|
||||
var ret, codewordlen;
|
||||
ret = golomb_decoding(buckets_ptrs[correlate_row[i_1]].bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
|
||||
var data = ret[0];
|
||||
codewordlen = ret[1];
|
||||
correlate_row[i] = data;
|
||||
|
||||
var ret = ((((xlatL2U[data] +
|
||||
((prev_pixel +
|
||||
result[offset + (i_4) + num_channel]) >>> 1))
|
||||
& bpc_mask
|
||||
)));
|
||||
|
||||
encoder.eatbits(codewordlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
function UNCOMPRESS_ONE_ROW0_0(channel) {
|
||||
var ret, codewordlen;
|
||||
var encoder = channel.encoder;
|
||||
ret = golomb_decoding(find_bucket(channel, 0).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
channel.correlate_row[0] = ret[0]
|
||||
codewordlen = ret[1];
|
||||
var ret = wdi.xlatL2U[channel.correlate_row[0]];
|
||||
encoder.eatbits(codewordlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function UNCOMPRESS_ONE_ROW0(channel, i, bpc_mask, encoder, correlate_row, prev_pixel) {
|
||||
var ret, codewordlen;
|
||||
ret = golomb_decoding(find_bucket(channel, correlate_row[i-1]).bestcode, encoder.io_word, wdi.JSQuic.bppmask);
|
||||
correlate_row[i] = ret[0];
|
||||
codewordlen = ret[1];
|
||||
var ret = CORELATE_0(encoder, channel, i, bpc_mask, correlate_row, prev_pixel);
|
||||
encoder.eatbits(codewordlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function CORELATE_0(encoder, channel, curr, bpc_mask, correlate_row, prev_pixel) {
|
||||
return ((wdi.xlatL2U[correlate_row[curr]] + prev_pixel) & bpc_mask);
|
||||
}
|
||||
|
||||
function PIXEL_A(encoder) {
|
||||
return encoder.result[encoder.cnt - 4];
|
||||
}
|
||||
|
||||
function PIXEL_B(channel, encoder, pos, offset) {
|
||||
return encoder.result[offset + (pos*4) + channel.num_channel];
|
||||
}
|
698
js/spice-web-client/lib/images/lz.js
Normal file
698
js/spice-web-client/lib/images/lz.js
Normal file
@ -0,0 +1,698 @@
|
||||
wdi.LZSS = {
|
||||
LZ_IMAGE_TYPE_INVALID: 0,
|
||||
LZ_IMAGE_TYPE_PLT1_LE: 1,
|
||||
LZ_IMAGE_TYPE_PLT1_BE: 2,
|
||||
LZ_IMAGE_TYPE_PLT4_LE: 3,
|
||||
LZ_IMAGE_TYPE_PLT4_BE: 4,
|
||||
LZ_IMAGE_TYPE_PLT8: 5,
|
||||
LZ_IMAGE_TYPE_RGB16: 6,
|
||||
LZ_IMAGE_TYPE_RGB24: 7,
|
||||
LZ_IMAGE_TYPE_RGB32: 8,
|
||||
LZ_IMAGE_TYPE_RGBA: 9,
|
||||
LZ_IMAGE_TYPE_XXXA: 10,
|
||||
LZPALETTE_FLAG_PAL_CACHE_ME: 1,
|
||||
LZPALETTE_FLAG_PAL_FROM_CACHE: 2,
|
||||
LZPALETTE_FLAG_TOP_DOWN: 4,
|
||||
PLT_PIXELS_PER_BYTE: [0, 8, 8, 2, 2, 1],
|
||||
PLT1_MASK: [1, 2, 4, 8, 16, 32, 64, 128],
|
||||
|
||||
copy_pixel: function(op, color, out_buf) {
|
||||
out_buf[(op) + 0] = color.r;
|
||||
out_buf[(op) + 1] = color.g;
|
||||
out_buf[(op) + 2] = color.b;
|
||||
},
|
||||
|
||||
lz_rgb32_decompress_rgb: function(arr) {
|
||||
//TODO: global alpha and uncouple code
|
||||
var encoder = 0;
|
||||
var op = 0;
|
||||
var ctrl;
|
||||
var in_buf = new Uint8Array(arr);
|
||||
var format = in_buf[encoder++];
|
||||
var opaque = in_buf[encoder++];
|
||||
var type = in_buf[encoder++];
|
||||
encoder++; //padding
|
||||
|
||||
var low = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
|
||||
encoder += 2;
|
||||
var high = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
|
||||
encoder += 2;
|
||||
var len = high*Math.pow(16, 4)+low;
|
||||
|
||||
var buf = new ArrayBuffer(len);
|
||||
var buf8 = new Uint8Array(buf);
|
||||
var data = new Uint32Array(buf);
|
||||
var out_buf_len = len/4;
|
||||
|
||||
var code, ref, len, ofs, ref_4, b_4, b;
|
||||
|
||||
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
|
||||
{
|
||||
ref = op;
|
||||
len = ctrl >> 5;
|
||||
ofs = ((ctrl & 31) << 8);
|
||||
|
||||
if (ctrl > 31) { //>=32
|
||||
len--;
|
||||
|
||||
if (len === 6) {
|
||||
do {
|
||||
code = in_buf[encoder++];
|
||||
len += code;
|
||||
} while (code === 255);
|
||||
}
|
||||
code = in_buf[encoder++];
|
||||
ofs += code;
|
||||
|
||||
|
||||
if (code === 255) {
|
||||
if ((ofs - code) === (31 << 8)) {
|
||||
ofs = in_buf[encoder++] << 8;
|
||||
ofs += in_buf[encoder++];
|
||||
ofs += 8191;
|
||||
}
|
||||
}
|
||||
len += 1;
|
||||
ofs += 1;
|
||||
ref -= ofs;
|
||||
|
||||
if (ref === (op - 1)) {//plt4/1 what?
|
||||
b = ref;
|
||||
b_4 = b*4;
|
||||
for (; len; --len) {
|
||||
data[op] =
|
||||
(255 << 24) | // alpha
|
||||
(buf8[(b_4)+2] << 16) | // blue
|
||||
(buf8[(b_4)+1] << 8) | // green
|
||||
buf8[(b_4)]; // red
|
||||
|
||||
op++;
|
||||
}
|
||||
} else {
|
||||
|
||||
for (; len; --len) {
|
||||
//COPY_REF_PIXEL
|
||||
ref_4 = ref*4;
|
||||
|
||||
data[op] =
|
||||
(255 << 24) | // alpha
|
||||
(buf8[(ref_4)+2] << 16) | // blue
|
||||
(buf8[(ref_4)+1] << 8) | // green
|
||||
buf8[(ref_4)]; // red
|
||||
|
||||
op++;ref++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//COPY_COMP_PIXEL
|
||||
ctrl++;
|
||||
|
||||
data[op] =
|
||||
(255 << 24) | // alpha
|
||||
(in_buf[encoder] << 16) | // blue
|
||||
(in_buf[encoder + 1] << 8) | // green
|
||||
in_buf[encoder + 2]; // red
|
||||
|
||||
encoder += 3;
|
||||
|
||||
op++;
|
||||
|
||||
|
||||
for (--ctrl; ctrl; ctrl--) {
|
||||
//COPY_COMP_PIXEL
|
||||
|
||||
data[op] =
|
||||
(255 << 24) | // alpha
|
||||
(in_buf[encoder] << 16) | // blue
|
||||
(in_buf[encoder + 1] << 8) | // green
|
||||
in_buf[encoder + 2]; // red
|
||||
encoder += 3;
|
||||
|
||||
op++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === this.LZ_IMAGE_TYPE_RGBA && !opaque) {
|
||||
|
||||
op = 0;
|
||||
ctrl = null;
|
||||
encoder--;
|
||||
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
|
||||
{
|
||||
var ref = op;
|
||||
var len = ctrl >> 5;
|
||||
var ofs = ((ctrl & 31) << 8);
|
||||
var op_4 = op*4;
|
||||
|
||||
if (ctrl >= 32) {
|
||||
|
||||
var code;
|
||||
len--;
|
||||
|
||||
if (len === 7 - 1) {
|
||||
do {
|
||||
code = in_buf[encoder++];
|
||||
len += code;
|
||||
} while (code === 255);
|
||||
}
|
||||
code = in_buf[encoder++];
|
||||
ofs += code;
|
||||
|
||||
|
||||
if (code === 255) {
|
||||
if ((ofs - code) === (31 << 8)) {
|
||||
ofs = in_buf[encoder++] << 8;
|
||||
ofs += in_buf[encoder++];
|
||||
ofs += 8191;
|
||||
}
|
||||
}
|
||||
len += 3;
|
||||
|
||||
ofs += 1;
|
||||
|
||||
ref -= ofs;
|
||||
if (ref === (op - 1)) {//plt4/1 what?
|
||||
var b = ref;
|
||||
|
||||
for (; len; --len) {
|
||||
op_4 = op*4;
|
||||
//COPY_PIXEL
|
||||
buf8[(op_4) + 3] = buf8[(b*4)+3];
|
||||
|
||||
op++;
|
||||
}
|
||||
} else {
|
||||
|
||||
for (; len; --len) {
|
||||
//COPY_REF_PIXEL
|
||||
op_4 = op*4;
|
||||
buf8[(op_4) + 3] = buf8[(ref*4)+3];
|
||||
|
||||
op++;ref++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//COPY_COMP_PIXEL
|
||||
ctrl++;
|
||||
buf8[(op_4) + 3] = in_buf[encoder++];
|
||||
op++;
|
||||
|
||||
|
||||
for (--ctrl; ctrl; ctrl--) {
|
||||
//COPY_COMP_PIXEL
|
||||
op_4 = op*4; // faster?
|
||||
buf8[(op_4) + 3] = in_buf[encoder++];
|
||||
op++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
|
||||
lz_rgb32_decompress: function(in_buf, at, out_buf, type, default_alpha, palette, opaque) {
|
||||
//TODO: global alpha and uncouple code
|
||||
var encoder = at;
|
||||
var op = 0;
|
||||
var ctrl;
|
||||
var out_buf_len = out_buf.length/4;
|
||||
var is_rgba = type === this.LZ_IMAGE_TYPE_RGBA?true:false;
|
||||
|
||||
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
|
||||
{
|
||||
var ref = op;
|
||||
var len = ctrl >> 5;
|
||||
var ofs = ((ctrl & 31) << 8);
|
||||
var op_4 = op*4;
|
||||
|
||||
if (ctrl >= 32) {
|
||||
|
||||
var code;
|
||||
len--;
|
||||
|
||||
if (len === 7 - 1) {
|
||||
do {
|
||||
code = in_buf[encoder++];
|
||||
len += code;
|
||||
} while (code === 255);
|
||||
}
|
||||
code = in_buf[encoder++];
|
||||
ofs += code;
|
||||
|
||||
|
||||
if (code === 255) {
|
||||
if ((ofs - code) === (31 << 8)) {
|
||||
ofs = in_buf[encoder++] << 8;
|
||||
ofs += in_buf[encoder++];
|
||||
ofs += 8191;
|
||||
}
|
||||
}
|
||||
len += 1;
|
||||
if (is_rgba || palette)
|
||||
len += 2;
|
||||
|
||||
ofs += 1;
|
||||
//CAST_PLT_DISTANCE ofs and len
|
||||
if (type === this.LZ_IMAGE_TYPE_PLT4_LE || type === this.LZ_IMAGE_TYPE_PLT4_BE) {
|
||||
ofs = ofs*2;
|
||||
len = len*2;
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE || type === this.LZ_IMAGE_TYPE_PLT1_LE) {
|
||||
ofs = ofs*8;
|
||||
len = len*8;
|
||||
}
|
||||
|
||||
ref -= ofs;
|
||||
if (ref === (op - 1)) {//plt4/1 what?
|
||||
var b = ref;
|
||||
|
||||
for (; len; --len) {
|
||||
op_4 = op*4;
|
||||
//COPY_PIXEL
|
||||
if (is_rgba)
|
||||
{
|
||||
if(opaque) {
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else {
|
||||
out_buf[(op_4) + 3] = out_buf[(b*4)+3];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
out_buf[(op_4) + i] = out_buf[(b*4)+i];
|
||||
}
|
||||
op++;
|
||||
}
|
||||
} else {
|
||||
|
||||
for (; len; --len) {
|
||||
//COPY_REF_PIXEL
|
||||
op_4 = op*4;
|
||||
if (is_rgba)
|
||||
{
|
||||
if(opaque) {
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else {
|
||||
out_buf[(op_4) + 3] = out_buf[(ref*4)+3];
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 4; i++)
|
||||
out_buf[(op_4) + i] = out_buf[(ref*4)+i];
|
||||
}
|
||||
op++;ref++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//COPY_COMP_PIXEL
|
||||
ctrl++;
|
||||
|
||||
if (is_rgba) {
|
||||
if(opaque) {
|
||||
out_buf[(op_4) + 3] = 255;encoder++;
|
||||
} else {
|
||||
out_buf[(op_4) + 3] = in_buf[encoder++];
|
||||
}
|
||||
} else if (palette) {
|
||||
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var foreColor = palette[1];
|
||||
var backColor = palette[0];
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
if (oct & this.PLT1_MASK[i]) {
|
||||
this.copy_pixel(op_4, foreColor, out_buf);
|
||||
} else {
|
||||
this.copy_pixel(op_4, backColor, out_buf);
|
||||
}
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
if (i < 7)
|
||||
op++;op_4 = op*4;
|
||||
}
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var foreColor = palette[1];
|
||||
var backColor = palette[0];
|
||||
|
||||
for (var i = 7; i >= 0; i--) {
|
||||
if (oct & this.PLT1_MASK[i]) {
|
||||
this.copy_pixel(op_4, foreColor, out_buf);
|
||||
} else {
|
||||
this.copy_pixel(op_4, backColor, out_buf);
|
||||
}
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
if (i > 0)
|
||||
op++;op_4 = op*4;
|
||||
}
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var spiceColor = palette[(oct & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
op++;
|
||||
op_4 = op*4;
|
||||
|
||||
var spiceColor = palette[((oct >>> 4) & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var bits1 = ((oct >>> 4) & 0x0f);
|
||||
var bits2 = oct & 0x0f;
|
||||
var spiceColor = palette[bits1];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
op++;
|
||||
op_4 = op*4;
|
||||
|
||||
var spiceColor = palette[bits2];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
|
||||
var posPal = in_buf[encoder++];
|
||||
var spiceColor = palette[posPal];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
out_buf[(op_4) + 0] = in_buf[encoder + 2];
|
||||
out_buf[(op_4) + 1] = in_buf[encoder + 1];
|
||||
out_buf[(op_4) + 2] = in_buf[encoder + 0];
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
encoder += 3;
|
||||
}
|
||||
op++;
|
||||
|
||||
|
||||
for (--ctrl; ctrl; ctrl--) {
|
||||
//COPY_COMP_PIXEL
|
||||
op_4 = op*4; // faster?
|
||||
if (is_rgba) {
|
||||
if(opaque) {
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else {
|
||||
out_buf[(op_4) + 3] = in_buf[encoder++];
|
||||
}
|
||||
} else if (palette) {
|
||||
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var foreColor = palette[1];
|
||||
var backColor = palette[0];
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
if (oct & this.PLT1_MASK[i]) {
|
||||
this.copy_pixel(op_4, foreColor, out_buf);
|
||||
} else {
|
||||
this.copy_pixel(op_4, backColor, out_buf);
|
||||
}
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
if (i < 7)
|
||||
op++;op_4 = op*4;
|
||||
}
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var foreColor = palette[1];
|
||||
var backColor = palette[0];
|
||||
|
||||
for (var i = 7; i >=0; i--) {
|
||||
if (oct & this.PLT1_MASK[i]) {
|
||||
this.copy_pixel(op_4, foreColor, out_buf);
|
||||
} else {
|
||||
this.copy_pixel(op_4, backColor, out_buf);
|
||||
}
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
if (i > 0)
|
||||
op++;op_4 = op*4;
|
||||
}
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var spiceColor = palette[(oct & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
op++;
|
||||
op_4 = op*4;
|
||||
var spiceColor = palette[((oct >>> 4) & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
|
||||
var oct = in_buf[encoder++];
|
||||
var spiceColor = palette[((oct >>> 4) & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
op++;
|
||||
op_4 = op*4;
|
||||
|
||||
var spiceColor = palette[(oct & 0x0f)];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
|
||||
var posPal = in_buf[encoder++];
|
||||
var spiceColor = palette[posPal];
|
||||
this.copy_pixel(op_4, spiceColor, out_buf);
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
}
|
||||
} else {
|
||||
out_buf[(op_4) + 0] = in_buf[encoder + 2];
|
||||
out_buf[(op_4) + 1] = in_buf[encoder + 1];
|
||||
out_buf[(op_4) + 2] = in_buf[encoder + 0];
|
||||
if (default_alpha)
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
encoder += 3;
|
||||
}
|
||||
op++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return encoder - 1;
|
||||
},
|
||||
|
||||
convert_spice_lz_to_web: function(context, data, imageDescriptor, opaque) { //TODO: refactor this shit code
|
||||
// var aux = data.toJSArray();
|
||||
var format = imageDescriptor.type;
|
||||
data = data.toJSArray(); //this old functions has no support for typed arrays...
|
||||
if (format === wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_PLT) {
|
||||
var flags = wdi.SpiceObject.bytesToInt8(data.splice(0, 1));
|
||||
if (flags === this.LZPALETTE_FLAG_PAL_FROM_CACHE) {
|
||||
var header = data.splice(0, 12);
|
||||
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
|
||||
var palette_id = wdi.SpiceObject.bytesToInt64(header.splice(0, 8));
|
||||
|
||||
header = data;
|
||||
|
||||
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
|
||||
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
|
||||
} else if (flags === this.LZPALETTE_FLAG_PAL_CACHE_ME) {
|
||||
var imageHeaders = imageDescriptor.offset + 1; //+1 because of the Flags byte
|
||||
var currentHeaders = 36;
|
||||
var header = data.splice(0, currentHeaders);
|
||||
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
|
||||
var palette_offset = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
|
||||
var spliceInit = palette_offset-imageHeaders-currentHeaders;
|
||||
//LZ Compression headers with its magic
|
||||
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
|
||||
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
|
||||
|
||||
var palette_id = wdi.SpiceObject.bytesToInt64(data.splice(spliceInit, 8));
|
||||
|
||||
var num_palettes = wdi.SpiceObject.bytesToInt16(data.splice(spliceInit, 2));
|
||||
var palette = [];
|
||||
|
||||
for (var i = 0; i < num_palettes; i++) {
|
||||
var queue = new wdi.Queue();
|
||||
queue.setData(data.splice(spliceInit, 4));
|
||||
palette.push(new wdi.SpiceColor().demarshall(queue));
|
||||
}
|
||||
wdi.ImageCache.addPalette(palette_id, palette);
|
||||
} else {
|
||||
wdi.Debug.error('Unimplemented lz palette top down');
|
||||
}
|
||||
var palette = wdi.ImageCache.getPalette(palette_id);
|
||||
}
|
||||
|
||||
if (type !== this.LZ_IMAGE_TYPE_RGB32 && type !== this.LZ_IMAGE_TYPE_RGBA &&
|
||||
type !== this.LZ_IMAGE_TYPE_RGB24 && type !== this.LZ_IMAGE_TYPE_PLT8 &&
|
||||
type !== this.LZ_IMAGE_TYPE_PLT1_LE && type !== this.LZ_IMAGE_TYPE_PLT1_BE &&
|
||||
type !== this.LZ_IMAGE_TYPE_PLT4_LE && type !== this.LZ_IMAGE_TYPE_PLT4_BE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (palette) {
|
||||
var ret = context.createImageData(stride*this.PLT_PIXELS_PER_BYTE[type], height);
|
||||
// if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
|
||||
// this.lz_rgb32_plt1_be_decompress(data, ret.data, palette);
|
||||
// } else {
|
||||
this.lz_rgb32_decompress(data, 0, ret.data, type, type !== this.LZ_IMAGE_TYPE_RGBA, palette);
|
||||
// }
|
||||
|
||||
var tmpCanvas = wdi.graphics.getNewTmpCanvas(width, height);
|
||||
tmpCanvas.getContext('2d').putImageData(ret, 0, 0, 0, 0, width, height);
|
||||
ret = tmpCanvas;
|
||||
} else {
|
||||
var arr = new ArrayBuffer(data.length+8);
|
||||
var u8 = new Uint8Array(arr);
|
||||
u8[0] = 1;
|
||||
u8[1] = opaque;
|
||||
u8[2] = type;
|
||||
u8[3] = 0;
|
||||
|
||||
var number = ret.data.length;
|
||||
|
||||
for (var i = 0;i < 4;i++) {//iterations because of javascript number size
|
||||
u8[4+i] = number & (255);//Get only the last byte
|
||||
number = number >> 8;//Remove the last byte
|
||||
}
|
||||
|
||||
u8.set(data, 8);
|
||||
var result = new Uint8ClampedArray(this.lz_rgb32_decompress_rgb(arr));
|
||||
ret = new ImageData(result, width, height);
|
||||
ret = wdi.graphics.getImageFromData(ret);
|
||||
}
|
||||
|
||||
if(!top_down) {
|
||||
//TODO: PERFORMANCE:
|
||||
ret = wdi.RasterOperation.flip(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
demarshall_rgb: function(data) {
|
||||
var header = data.splice(0, 32);
|
||||
return {
|
||||
length: wdi.SpiceObject.bytesToInt32(header.splice(0,4)),
|
||||
magic: wdi.SpiceObject.bytesToStringBE(header.splice(0,4)),
|
||||
version: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
|
||||
type: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
|
||||
width: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
|
||||
height: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
|
||||
stride: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
|
||||
top_down: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4))
|
||||
}
|
||||
},
|
||||
|
||||
lz_rgb32_plt1_be_decompress: function(in_buf, out_buf, palette) {
|
||||
var encoder = 0;
|
||||
var op = 0;
|
||||
var ctrl;
|
||||
var out_buf_len = out_buf.length/4;
|
||||
var ref, len, ofs, next, ref_4, oct, foreColor, backColor, i;
|
||||
var type = this.LZ_IMAGE_TYPE_PLT1_BE;
|
||||
|
||||
var pix_per_byte = this.PLT_PIXELS_PER_BYTE[type];
|
||||
|
||||
var pre_255_24 = 255 << 24;
|
||||
var pre_31_8_plus255 = (31 << 8) + 255; //8191 === 13 bits to 1
|
||||
|
||||
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++]) {
|
||||
ref = op;
|
||||
len = ctrl >> 5;
|
||||
ofs = ((ctrl & 31) << 8);
|
||||
|
||||
if (ctrl > 31) {
|
||||
|
||||
if (len === 7) {
|
||||
do {
|
||||
next = in_buf[encoder++];
|
||||
len += next;
|
||||
} while (next === 255);
|
||||
}
|
||||
|
||||
ofs += in_buf[encoder++];
|
||||
|
||||
if (ofs === pre_31_8_plus255) {
|
||||
ofs += in_buf[encoder++] << 8 + in_buf[encoder++];
|
||||
}
|
||||
|
||||
//CAST_PLT_DISTANCE ofs and len
|
||||
len = (len + 2) * pix_per_byte;
|
||||
|
||||
ref -= (ofs + 1) * pix_per_byte;
|
||||
if (ref === (op - 1)) {
|
||||
ref_4 = ref * 4;
|
||||
while (len-- !== 0) {
|
||||
//COPY_PIXEL
|
||||
op_4 = op * 4;
|
||||
|
||||
out_buf[op_4] = out_buf[ref_4];
|
||||
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
|
||||
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
|
||||
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
|
||||
|
||||
op++;
|
||||
}
|
||||
} else {
|
||||
while (len-- !== 0) {
|
||||
//COPY_REF_PIXEL
|
||||
op_4 = op * 4;
|
||||
ref_4 = ref * 4;
|
||||
|
||||
out_buf[op_4] = out_buf[ref_4];
|
||||
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
|
||||
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
|
||||
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
|
||||
|
||||
op++;ref++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//COPY_COMP_PIXEL
|
||||
while (ctrl-- !== -1) {
|
||||
//COPY_COMP_PIXEL
|
||||
op_4 = op * 4; // faster?
|
||||
|
||||
oct = in_buf[encoder++];
|
||||
foreColor = palette[1];
|
||||
backColor = palette[0];
|
||||
|
||||
for (i = 7; i >=0; i--) {
|
||||
op_4 = op * 4;
|
||||
|
||||
if (oct & this.PLT1_MASK[i]) {
|
||||
out_buf[op_4 + 0] = foreColor.r;
|
||||
out_buf[op_4 + 1] = foreColor.g;
|
||||
out_buf[op_4 + 2] = foreColor.b;
|
||||
} else {
|
||||
out_buf[op_4 + 0] = backColor.r;
|
||||
out_buf[op_4 + 1] = backColor.g;
|
||||
out_buf[op_4 + 2] = backColor.b;
|
||||
}
|
||||
out_buf[(op_4) + 3] = 255;
|
||||
|
||||
op++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
256
js/spice-web-client/lib/images/png.js
Normal file
256
js/spice-web-client/lib/images/png.js
Normal file
@ -0,0 +1,256 @@
|
||||
"use strict";
|
||||
/*
|
||||
Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
|
||||
|
||||
This file is part of spice-html5.
|
||||
|
||||
spice-html5 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
spice-html5 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
** crc logic from rfc2083 ported to Javascript
|
||||
**--------------------------------------------------------------------------*/
|
||||
|
||||
var rfc2083_crc_table = Array(256);
|
||||
var rfc2083_crc_table_computed = 0;
|
||||
/* Make the table for a fast CRC. */
|
||||
function rfc2083_make_crc_table()
|
||||
{
|
||||
var c;
|
||||
var n, k;
|
||||
for (n = 0; n < 256; n++)
|
||||
{
|
||||
c = n;
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
if (c & 1)
|
||||
c = ((0xedb88320 ^ (c >>> 1)) >>> 0) & 0xffffffff;
|
||||
else
|
||||
c = c >>> 1;
|
||||
}
|
||||
rfc2083_crc_table[n] = c;
|
||||
}
|
||||
|
||||
rfc2083_crc_table_computed = 1;
|
||||
}
|
||||
|
||||
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
|
||||
should be initialized to all 1's, and the transmitted value
|
||||
is the 1's complement of the final running CRC (see the
|
||||
crc() routine below)). */
|
||||
|
||||
function rfc2083_update_crc(crc, u8buf, at, len)
|
||||
{
|
||||
var c = crc;
|
||||
var n;
|
||||
|
||||
if (!rfc2083_crc_table_computed)
|
||||
rfc2083_make_crc_table();
|
||||
|
||||
for (n = 0; n < len; n++)
|
||||
{
|
||||
c = rfc2083_crc_table[(c ^ u8buf[at + n]) & 0xff] ^ (c >>> 8);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
function rfc2083_crc(u8buf, at, len)
|
||||
{
|
||||
return rfc2083_update_crc(0xffffffff, u8buf, at, len) ^ 0xffffffff;
|
||||
}
|
||||
|
||||
function crc32(mb, at, len)
|
||||
{
|
||||
var u8 = new Uint8Array(mb);
|
||||
return rfc2083_crc(u8, at, len);
|
||||
}
|
||||
|
||||
function PngIHDR(width, height)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = 8;
|
||||
this.type = 6;
|
||||
this.compression = 0;
|
||||
this.filter = 0;
|
||||
this.interlace = 0;
|
||||
}
|
||||
|
||||
PngIHDR.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var orig = at;
|
||||
var dv = new SpiceDataView(a);
|
||||
dv.setUint32(at, this.buffer_size() - 12); at += 4;
|
||||
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'H'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'R'.charCodeAt(0)); at++;
|
||||
dv.setUint32(at, this.width); at += 4;
|
||||
dv.setUint32(at, this.height); at += 4;
|
||||
dv.setUint8(at, this.depth); at++;
|
||||
dv.setUint8(at, this.type); at++;
|
||||
dv.setUint8(at, this.compression); at++;
|
||||
dv.setUint8(at, this.filter); at++;
|
||||
dv.setUint8(at, this.interlace); at++;
|
||||
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
|
||||
return at;
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 12 + 13;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function adler()
|
||||
{
|
||||
this.s1 = 1;
|
||||
this.s2 = 0;
|
||||
}
|
||||
|
||||
adler.prototype.update = function(b)
|
||||
{
|
||||
this.s1 += b;
|
||||
this.s1 %= 65521;
|
||||
this.s2 += this.s1;
|
||||
this.s2 %= 65521;
|
||||
}
|
||||
|
||||
function PngIDAT(width, height, bytes)
|
||||
{
|
||||
if (bytes.byteLength > 65535)
|
||||
{
|
||||
throw new Error("Cannot handle more than 64K");
|
||||
}
|
||||
this.data = bytes;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
PngIDAT.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var orig = at;
|
||||
var x, y, i, j;
|
||||
var dv = new SpiceDataView(a);
|
||||
var zsum = new adler();
|
||||
dv.setUint32(at, this.buffer_size() - 12); at += 4;
|
||||
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'A'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'T'.charCodeAt(0)); at++;
|
||||
|
||||
/* zlib header. */
|
||||
dv.setUint8(at, 0x78); at++;
|
||||
dv.setUint8(at, 0x01); at++;
|
||||
|
||||
/* Deflate header. Specifies uncompressed, final bit */
|
||||
dv.setUint8(at, 0x80); at++;
|
||||
dv.setUint16(at, this.data.byteLength + this.height); at += 2;
|
||||
dv.setUint16(at, ~(this.data.byteLength + this.height)); at += 2;
|
||||
var u8 = new Uint8Array(this.data);
|
||||
for (i = 0, y = 0; y < this.height; y++)
|
||||
{
|
||||
/* Filter type 0 - uncompressed */
|
||||
dv.setUint8(at, 0); at++;
|
||||
zsum.update(0);
|
||||
for (x = 0; x < this.width && i < this.data.byteLength; x++)
|
||||
{
|
||||
zsum.update(u8[i]);
|
||||
dv.setUint8(at, u8[i++]); at++;
|
||||
zsum.update(u8[i]);
|
||||
dv.setUint8(at, u8[i++]); at++;
|
||||
zsum.update(u8[i]);
|
||||
dv.setUint8(at, u8[i++]); at++;
|
||||
zsum.update(u8[i]);
|
||||
dv.setUint8(at, u8[i++]); at++;
|
||||
}
|
||||
}
|
||||
|
||||
/* zlib checksum. */
|
||||
dv.setUint16(at, zsum.s2); at+=2;
|
||||
dv.setUint16(at, zsum.s1); at+=2;
|
||||
|
||||
/* FIXME - something is not quite right with the zlib code;
|
||||
you get an error from libpng if you open the image in
|
||||
gimp. But it works, so it's good enough for now... */
|
||||
|
||||
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
|
||||
return at;
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 12 + this.data.byteLength + this.height + 4 + 2 + 1 + 2 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function PngIEND()
|
||||
{
|
||||
}
|
||||
|
||||
PngIEND.prototype =
|
||||
{
|
||||
to_buffer: function(a, at)
|
||||
{
|
||||
at = at || 0;
|
||||
var orig = at;
|
||||
var i;
|
||||
var dv = new SpiceDataView(a);
|
||||
dv.setUint32(at, this.buffer_size() - 12); at += 4;
|
||||
dv.setUint8(at, 'I'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'E'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'N'.charCodeAt(0)); at++;
|
||||
dv.setUint8(at, 'D'.charCodeAt(0)); at++;
|
||||
dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4;
|
||||
return at;
|
||||
},
|
||||
buffer_size: function()
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function create_rgba_png(width, height, bytes)
|
||||
{
|
||||
var i;
|
||||
var ihdr = new PngIHDR(width, height);
|
||||
var idat = new PngIDAT(width, height, bytes);
|
||||
var iend = new PngIEND;
|
||||
|
||||
var mb = new ArrayBuffer(ihdr.buffer_size() + idat.buffer_size() + iend.buffer_size());
|
||||
var at = ihdr.to_buffer(mb);
|
||||
at = idat.to_buffer(mb, at);
|
||||
at = iend.to_buffer(mb, at);
|
||||
|
||||
var u8 = new Uint8Array(mb);
|
||||
var str = "";
|
||||
for (i = 0; i < at; i++)
|
||||
{
|
||||
str += "%";
|
||||
if (u8[i] < 16)
|
||||
str += "0";
|
||||
str += u8[i].toString(16);
|
||||
}
|
||||
|
||||
|
||||
return "%89PNG%0D%0A%1A%0A" + str;
|
||||
}
|
25
js/spice-web-client/lib/jgestures.min.js
vendored
Normal file
25
js/spice-web-client/lib/jgestures.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8829
js/spice-web-client/lib/jquery-2.0.3.js
vendored
Normal file
8829
js/spice-web-client/lib/jquery-2.0.3.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
84
js/spice-web-client/lib/jquery-mousewheel.js
vendored
Normal file
84
js/spice-web-client/lib/jquery-mousewheel.js
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
|
||||
* Licensed under the MIT License (LICENSE.txt).
|
||||
*
|
||||
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
|
||||
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
|
||||
* Thanks to: Seamus Leahy for adding deltaX and deltaY
|
||||
*
|
||||
* Version: 3.0.6
|
||||
*
|
||||
* Requires: 1.2.2+
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
var types = ['DOMMouseScroll', 'mousewheel'];
|
||||
|
||||
if ($.event.fixHooks) {
|
||||
for ( var i=types.length; i; ) {
|
||||
$.event.fixHooks[ types[--i] ] = $.event.mouseHooks;
|
||||
}
|
||||
}
|
||||
|
||||
$.event.special.mousewheel = {
|
||||
setup: function() {
|
||||
if ( this.addEventListener ) {
|
||||
for ( var i=types.length; i; ) {
|
||||
this.addEventListener( types[--i], handler, false );
|
||||
}
|
||||
} else {
|
||||
this.onmousewheel = handler;
|
||||
}
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
if ( this.removeEventListener ) {
|
||||
for ( var i=types.length; i; ) {
|
||||
this.removeEventListener( types[--i], handler, false );
|
||||
}
|
||||
} else {
|
||||
this.onmousewheel = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.extend({
|
||||
mousewheel: function(fn) {
|
||||
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
|
||||
},
|
||||
|
||||
unmousewheel: function(fn) {
|
||||
return this.unbind("mousewheel", fn);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function handler(event) {
|
||||
var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
|
||||
event = $.event.fix(orgEvent);
|
||||
event.type = "mousewheel";
|
||||
|
||||
// Old school scrollwheel delta
|
||||
if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta/120; }
|
||||
if ( orgEvent.detail ) { delta = -orgEvent.detail/3; }
|
||||
|
||||
// New school multidimensional scroll (touchpads) deltas
|
||||
deltaY = delta;
|
||||
|
||||
// Gecko
|
||||
if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
|
||||
deltaY = 0;
|
||||
deltaX = -1*delta;
|
||||
}
|
||||
|
||||
// Webkit
|
||||
if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
|
||||
if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
|
||||
|
||||
// Add event and delta to the front of the arguments
|
||||
args.unshift(event, delta, deltaX, deltaY);
|
||||
|
||||
return ($.event.dispatch || $.event.handle).apply(this, args);
|
||||
}
|
||||
|
||||
})(jQuery);
|
559
js/spice-web-client/lib/jsbn.js
Normal file
559
js/spice-web-client/lib/jsbn.js
Normal file
@ -0,0 +1,559 @@
|
||||
// Copyright (c) 2005 Tom Wu
|
||||
// All Rights Reserved.
|
||||
// See "LICENSE" for details.
|
||||
|
||||
// Basic JavaScript BN library - subset useful for RSA encryption.
|
||||
|
||||
// Bits per digit
|
||||
var dbits;
|
||||
|
||||
// JavaScript engine analysis
|
||||
var canary = 0xdeadbeefcafe;
|
||||
var j_lm = ((canary&0xffffff)==0xefcafe);
|
||||
|
||||
// (public) Constructor
|
||||
function encryptionBigInteger(a,b,c) {
|
||||
if(a != null)
|
||||
if("number" == typeof a) this.fromNumber(a,b,c);
|
||||
else if(b == null && "string" != typeof a) this.fromString(a,256);
|
||||
else this.fromString(a,b);
|
||||
}
|
||||
|
||||
// return new, unset encryptionBigInteger
|
||||
function nbi() { return new encryptionBigInteger(null); }
|
||||
|
||||
// am: Compute w_j += (x*this_i), propagate carries,
|
||||
// c is initial carry, returns final carry.
|
||||
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
|
||||
// We need to select the fastest one that works in this environment.
|
||||
|
||||
// am1: use a single mult and divide to get the high bits,
|
||||
// max digit bits should be 26 because
|
||||
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
|
||||
function am1(i,x,w,j,c,n) {
|
||||
while(--n >= 0) {
|
||||
var v = x*this[i++]+w[j]+c;
|
||||
c = Math.floor(v/0x4000000);
|
||||
w[j++] = v&0x3ffffff;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
// am2 avoids a big mult-and-extract completely.
|
||||
// Max digit bits should be <= 30 because we do bitwise ops
|
||||
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
|
||||
function am2(i,x,w,j,c,n) {
|
||||
var xl = x&0x7fff, xh = x>>15;
|
||||
while(--n >= 0) {
|
||||
var l = this[i]&0x7fff;
|
||||
var h = this[i++]>>15;
|
||||
var m = xh*l+h*xl;
|
||||
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
|
||||
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
|
||||
w[j++] = l&0x3fffffff;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
// Alternately, set max digit bits to 28 since some
|
||||
// browsers slow down when dealing with 32-bit numbers.
|
||||
function am3(i,x,w,j,c,n) {
|
||||
var xl = x&0x3fff, xh = x>>14;
|
||||
while(--n >= 0) {
|
||||
var l = this[i]&0x3fff;
|
||||
var h = this[i++]>>14;
|
||||
var m = xh*l+h*xl;
|
||||
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
|
||||
c = (l>>28)+(m>>14)+xh*h;
|
||||
w[j++] = l&0xfffffff;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
|
||||
encryptionBigInteger.prototype.am = am2;
|
||||
dbits = 30;
|
||||
}
|
||||
else if(j_lm && (navigator.appName != "Netscape")) {
|
||||
encryptionBigInteger.prototype.am = am1;
|
||||
dbits = 26;
|
||||
}
|
||||
else { // Mozilla/Netscape seems to prefer am3
|
||||
encryptionBigInteger.prototype.am = am3;
|
||||
dbits = 28;
|
||||
}
|
||||
|
||||
encryptionBigInteger.prototype.DB = dbits;
|
||||
encryptionBigInteger.prototype.DM = ((1<<dbits)-1);
|
||||
encryptionBigInteger.prototype.DV = (1<<dbits);
|
||||
|
||||
var BI_FP = 52;
|
||||
encryptionBigInteger.prototype.FV = Math.pow(2,BI_FP);
|
||||
encryptionBigInteger.prototype.F1 = BI_FP-dbits;
|
||||
encryptionBigInteger.prototype.F2 = 2*dbits-BI_FP;
|
||||
|
||||
// Digit conversions
|
||||
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
var BI_RC = new Array();
|
||||
var rr,vv;
|
||||
rr = "0".charCodeAt(0);
|
||||
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
|
||||
rr = "a".charCodeAt(0);
|
||||
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
|
||||
rr = "A".charCodeAt(0);
|
||||
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
|
||||
|
||||
function int2char(n) { return BI_RM.charAt(n); }
|
||||
function intAt(s,i) {
|
||||
var c = BI_RC[s.charCodeAt(i)];
|
||||
return (c==null)?-1:c;
|
||||
}
|
||||
|
||||
// (protected) copy this to r
|
||||
function bnpCopyTo(r) {
|
||||
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
|
||||
r.t = this.t;
|
||||
r.s = this.s;
|
||||
}
|
||||
|
||||
// (protected) set from integer value x, -DV <= x < DV
|
||||
function bnpFromInt(x) {
|
||||
this.t = 1;
|
||||
this.s = (x<0)?-1:0;
|
||||
if(x > 0) this[0] = x;
|
||||
else if(x < -1) this[0] = x+DV;
|
||||
else this.t = 0;
|
||||
}
|
||||
|
||||
// return bigint initialized to value
|
||||
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
|
||||
|
||||
// (protected) set from string and radix
|
||||
function bnpFromString(s,b) {
|
||||
var k;
|
||||
if(b == 16) k = 4;
|
||||
else if(b == 8) k = 3;
|
||||
else if(b == 256) k = 8; // byte array
|
||||
else if(b == 2) k = 1;
|
||||
else if(b == 32) k = 5;
|
||||
else if(b == 4) k = 2;
|
||||
else { this.fromRadix(s,b); return; }
|
||||
this.t = 0;
|
||||
this.s = 0;
|
||||
var i = s.length, mi = false, sh = 0;
|
||||
while(--i >= 0) {
|
||||
var x = (k==8)?s[i]&0xff:intAt(s,i);
|
||||
if(x < 0) {
|
||||
if(s.charAt(i) == "-") mi = true;
|
||||
continue;
|
||||
}
|
||||
mi = false;
|
||||
if(sh == 0)
|
||||
this[this.t++] = x;
|
||||
else if(sh+k > this.DB) {
|
||||
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
|
||||
this[this.t++] = (x>>(this.DB-sh));
|
||||
}
|
||||
else
|
||||
this[this.t-1] |= x<<sh;
|
||||
sh += k;
|
||||
if(sh >= this.DB) sh -= this.DB;
|
||||
}
|
||||
if(k == 8 && (s[0]&0x80) != 0) {
|
||||
this.s = -1;
|
||||
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
|
||||
}
|
||||
this.clamp();
|
||||
if(mi) encryptionBigInteger.ZERO.subTo(this,this);
|
||||
}
|
||||
|
||||
// (protected) clamp off excess high words
|
||||
function bnpClamp() {
|
||||
var c = this.s&this.DM;
|
||||
while(this.t > 0 && this[this.t-1] == c) --this.t;
|
||||
}
|
||||
|
||||
// (public) return string representation in given radix
|
||||
function bnToString(b) {
|
||||
if(this.s < 0) return "-"+this.negate().toString(b);
|
||||
var k;
|
||||
if(b == 16) k = 4;
|
||||
else if(b == 8) k = 3;
|
||||
else if(b == 2) k = 1;
|
||||
else if(b == 32) k = 5;
|
||||
else if(b == 4) k = 2;
|
||||
else return this.toRadix(b);
|
||||
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
|
||||
var p = this.DB-(i*this.DB)%k;
|
||||
if(i-- > 0) {
|
||||
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
|
||||
while(i >= 0) {
|
||||
if(p < k) {
|
||||
d = (this[i]&((1<<p)-1))<<(k-p);
|
||||
d |= this[--i]>>(p+=this.DB-k);
|
||||
}
|
||||
else {
|
||||
d = (this[i]>>(p-=k))&km;
|
||||
if(p <= 0) { p += this.DB; --i; }
|
||||
}
|
||||
if(d > 0) m = true;
|
||||
if(m) r += int2char(d);
|
||||
}
|
||||
}
|
||||
return m?r:"0";
|
||||
}
|
||||
|
||||
// (public) -this
|
||||
function bnNegate() { var r = nbi(); encryptionBigInteger.ZERO.subTo(this,r); return r; }
|
||||
|
||||
// (public) |this|
|
||||
function bnAbs() { return (this.s<0)?this.negate():this; }
|
||||
|
||||
// (public) return + if this > a, - if this < a, 0 if equal
|
||||
function bnCompareTo(a) {
|
||||
var r = this.s-a.s;
|
||||
if(r != 0) return r;
|
||||
var i = this.t;
|
||||
r = i-a.t;
|
||||
if(r != 0) return (this.s<0)?-r:r;
|
||||
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns bit length of the integer x
|
||||
function nbits(x) {
|
||||
var r = 1, t;
|
||||
if((t=x>>>16) != 0) { x = t; r += 16; }
|
||||
if((t=x>>8) != 0) { x = t; r += 8; }
|
||||
if((t=x>>4) != 0) { x = t; r += 4; }
|
||||
if((t=x>>2) != 0) { x = t; r += 2; }
|
||||
if((t=x>>1) != 0) { x = t; r += 1; }
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) return the number of bits in "this"
|
||||
function bnBitLength() {
|
||||
if(this.t <= 0) return 0;
|
||||
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
|
||||
}
|
||||
|
||||
// (protected) r = this << n*DB
|
||||
function bnpDLShiftTo(n,r) {
|
||||
var i;
|
||||
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
|
||||
for(i = n-1; i >= 0; --i) r[i] = 0;
|
||||
r.t = this.t+n;
|
||||
r.s = this.s;
|
||||
}
|
||||
|
||||
// (protected) r = this >> n*DB
|
||||
function bnpDRShiftTo(n,r) {
|
||||
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
|
||||
r.t = Math.max(this.t-n,0);
|
||||
r.s = this.s;
|
||||
}
|
||||
|
||||
// (protected) r = this << n
|
||||
function bnpLShiftTo(n,r) {
|
||||
var bs = n%this.DB;
|
||||
var cbs = this.DB-bs;
|
||||
var bm = (1<<cbs)-1;
|
||||
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
|
||||
for(i = this.t-1; i >= 0; --i) {
|
||||
r[i+ds+1] = (this[i]>>cbs)|c;
|
||||
c = (this[i]&bm)<<bs;
|
||||
}
|
||||
for(i = ds-1; i >= 0; --i) r[i] = 0;
|
||||
r[ds] = c;
|
||||
r.t = this.t+ds+1;
|
||||
r.s = this.s;
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (protected) r = this >> n
|
||||
function bnpRShiftTo(n,r) {
|
||||
r.s = this.s;
|
||||
var ds = Math.floor(n/this.DB);
|
||||
if(ds >= this.t) { r.t = 0; return; }
|
||||
var bs = n%this.DB;
|
||||
var cbs = this.DB-bs;
|
||||
var bm = (1<<bs)-1;
|
||||
r[0] = this[ds]>>bs;
|
||||
for(var i = ds+1; i < this.t; ++i) {
|
||||
r[i-ds-1] |= (this[i]&bm)<<cbs;
|
||||
r[i-ds] = this[i]>>bs;
|
||||
}
|
||||
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
|
||||
r.t = this.t-ds;
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (protected) r = this - a
|
||||
function bnpSubTo(a,r) {
|
||||
var i = 0, c = 0, m = Math.min(a.t,this.t);
|
||||
while(i < m) {
|
||||
c += this[i]-a[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
if(a.t < this.t) {
|
||||
c -= a.s;
|
||||
while(i < this.t) {
|
||||
c += this[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
c += this.s;
|
||||
}
|
||||
else {
|
||||
c += this.s;
|
||||
while(i < a.t) {
|
||||
c -= a[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
c -= a.s;
|
||||
}
|
||||
r.s = (c<0)?-1:0;
|
||||
if(c < -1) r[i++] = this.DV+c;
|
||||
else if(c > 0) r[i++] = c;
|
||||
r.t = i;
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (protected) r = this * a, r != this,a (HAC 14.12)
|
||||
// "this" should be the larger one if appropriate.
|
||||
function bnpMultiplyTo(a,r) {
|
||||
var x = this.abs(), y = a.abs();
|
||||
var i = x.t;
|
||||
r.t = i+y.t;
|
||||
while(--i >= 0) r[i] = 0;
|
||||
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
|
||||
r.s = 0;
|
||||
r.clamp();
|
||||
if(this.s != a.s) encryptionBigInteger.ZERO.subTo(r,r);
|
||||
}
|
||||
|
||||
// (protected) r = this^2, r != this (HAC 14.16)
|
||||
function bnpSquareTo(r) {
|
||||
var x = this.abs();
|
||||
var i = r.t = 2*x.t;
|
||||
while(--i >= 0) r[i] = 0;
|
||||
for(i = 0; i < x.t-1; ++i) {
|
||||
var c = x.am(i,x[i],r,2*i,0,1);
|
||||
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
|
||||
r[i+x.t] -= x.DV;
|
||||
r[i+x.t+1] = 1;
|
||||
}
|
||||
}
|
||||
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
|
||||
r.s = 0;
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
|
||||
// r != q, this != m. q or r may be null.
|
||||
function bnpDivRemTo(m,q,r) {
|
||||
var pm = m.abs();
|
||||
if(pm.t <= 0) return;
|
||||
var pt = this.abs();
|
||||
if(pt.t < pm.t) {
|
||||
if(q != null) q.fromInt(0);
|
||||
if(r != null) this.copyTo(r);
|
||||
return;
|
||||
}
|
||||
if(r == null) r = nbi();
|
||||
var y = nbi(), ts = this.s, ms = m.s;
|
||||
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
|
||||
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
|
||||
else { pm.copyTo(y); pt.copyTo(r); }
|
||||
var ys = y.t;
|
||||
var y0 = y[ys-1];
|
||||
if(y0 == 0) return;
|
||||
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
|
||||
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
|
||||
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
|
||||
y.dlShiftTo(j,t);
|
||||
if(r.compareTo(t) >= 0) {
|
||||
r[r.t++] = 1;
|
||||
r.subTo(t,r);
|
||||
}
|
||||
encryptionBigInteger.ONE.dlShiftTo(ys,t);
|
||||
t.subTo(y,y); // "negative" y so we can replace sub with am later
|
||||
while(y.t < ys) y[y.t++] = 0;
|
||||
while(--j >= 0) {
|
||||
// Estimate quotient digit
|
||||
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
|
||||
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
|
||||
y.dlShiftTo(j,t);
|
||||
r.subTo(t,r);
|
||||
while(r[i] < --qd) r.subTo(t,r);
|
||||
}
|
||||
}
|
||||
if(q != null) {
|
||||
r.drShiftTo(ys,q);
|
||||
if(ts != ms) encryptionBigInteger.ZERO.subTo(q,q);
|
||||
}
|
||||
r.t = ys;
|
||||
r.clamp();
|
||||
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
|
||||
if(ts < 0) encryptionBigInteger.ZERO.subTo(r,r);
|
||||
}
|
||||
|
||||
// (public) this mod a
|
||||
function bnMod(a) {
|
||||
var r = nbi();
|
||||
this.abs().divRemTo(a,null,r);
|
||||
if(this.s < 0 && r.compareTo(encryptionBigInteger.ZERO) > 0) a.subTo(r,r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Modular reduction using "classic" algorithm
|
||||
function Classic(m) { this.m = m; }
|
||||
function cConvert(x) {
|
||||
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
|
||||
else return x;
|
||||
}
|
||||
function cRevert(x) { return x; }
|
||||
function cReduce(x) { x.divRemTo(this.m,null,x); }
|
||||
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
|
||||
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
|
||||
|
||||
Classic.prototype.convert = cConvert;
|
||||
Classic.prototype.revert = cRevert;
|
||||
Classic.prototype.reduce = cReduce;
|
||||
Classic.prototype.mulTo = cMulTo;
|
||||
Classic.prototype.sqrTo = cSqrTo;
|
||||
|
||||
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
|
||||
// justification:
|
||||
// xy == 1 (mod m)
|
||||
// xy = 1+km
|
||||
// xy(2-xy) = (1+km)(1-km)
|
||||
// x[y(2-xy)] = 1-k^2m^2
|
||||
// x[y(2-xy)] == 1 (mod m^2)
|
||||
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
|
||||
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
|
||||
// JS multiply "overflows" differently from C/C++, so care is needed here.
|
||||
function bnpInvDigit() {
|
||||
if(this.t < 1) return 0;
|
||||
var x = this[0];
|
||||
if((x&1) == 0) return 0;
|
||||
var y = x&3; // y == 1/x mod 2^2
|
||||
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
|
||||
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
|
||||
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
|
||||
// last step - calculate inverse mod DV directly;
|
||||
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
|
||||
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
|
||||
// we really want the negative inverse, and -DV < y < DV
|
||||
return (y>0)?this.DV-y:-y;
|
||||
}
|
||||
|
||||
// Montgomery reduction
|
||||
function Montgomery(m) {
|
||||
this.m = m;
|
||||
this.mp = m.invDigit();
|
||||
this.mpl = this.mp&0x7fff;
|
||||
this.mph = this.mp>>15;
|
||||
this.um = (1<<(m.DB-15))-1;
|
||||
this.mt2 = 2*m.t;
|
||||
}
|
||||
|
||||
// xR mod m
|
||||
function montConvert(x) {
|
||||
var r = nbi();
|
||||
x.abs().dlShiftTo(this.m.t,r);
|
||||
r.divRemTo(this.m,null,r);
|
||||
if(x.s < 0 && r.compareTo(encryptionBigInteger.ZERO) > 0) this.m.subTo(r,r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// x/R mod m
|
||||
function montRevert(x) {
|
||||
var r = nbi();
|
||||
x.copyTo(r);
|
||||
this.reduce(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// x = x/R mod m (HAC 14.32)
|
||||
function montReduce(x) {
|
||||
while(x.t <= this.mt2) // pad x so am has enough room later
|
||||
x[x.t++] = 0;
|
||||
for(var i = 0; i < this.m.t; ++i) {
|
||||
// faster way of calculating u0 = x[i]*mp mod DV
|
||||
var j = x[i]&0x7fff;
|
||||
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
|
||||
// use am to combine the multiply-shift-add into one call
|
||||
j = i+this.m.t;
|
||||
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
|
||||
// propagate carry
|
||||
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
|
||||
}
|
||||
x.clamp();
|
||||
x.drShiftTo(this.m.t,x);
|
||||
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
|
||||
}
|
||||
|
||||
// r = "x^2/R mod m"; x != r
|
||||
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
|
||||
|
||||
// r = "xy/R mod m"; x,y != r
|
||||
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
|
||||
|
||||
Montgomery.prototype.convert = montConvert;
|
||||
Montgomery.prototype.revert = montRevert;
|
||||
Montgomery.prototype.reduce = montReduce;
|
||||
Montgomery.prototype.mulTo = montMulTo;
|
||||
Montgomery.prototype.sqrTo = montSqrTo;
|
||||
|
||||
// (protected) true iff this is even
|
||||
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
|
||||
|
||||
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
|
||||
function bnpExp(e,z) {
|
||||
if(e > 0xffffffff || e < 1) return encryptionBigInteger.ONE;
|
||||
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
|
||||
g.copyTo(r);
|
||||
while(--i >= 0) {
|
||||
z.sqrTo(r,r2);
|
||||
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
|
||||
else { var t = r; r = r2; r2 = t; }
|
||||
}
|
||||
return z.revert(r);
|
||||
}
|
||||
|
||||
// (public) this^e % m, 0 <= e < 2^32
|
||||
function bnModPowInt(e,m) {
|
||||
var z;
|
||||
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
|
||||
return this.exp(e,z);
|
||||
}
|
||||
|
||||
// protected
|
||||
encryptionBigInteger.prototype.copyTo = bnpCopyTo;
|
||||
encryptionBigInteger.prototype.fromInt = bnpFromInt;
|
||||
encryptionBigInteger.prototype.fromString = bnpFromString;
|
||||
encryptionBigInteger.prototype.clamp = bnpClamp;
|
||||
encryptionBigInteger.prototype.dlShiftTo = bnpDLShiftTo;
|
||||
encryptionBigInteger.prototype.drShiftTo = bnpDRShiftTo;
|
||||
encryptionBigInteger.prototype.lShiftTo = bnpLShiftTo;
|
||||
encryptionBigInteger.prototype.rShiftTo = bnpRShiftTo;
|
||||
encryptionBigInteger.prototype.subTo = bnpSubTo;
|
||||
encryptionBigInteger.prototype.multiplyTo = bnpMultiplyTo;
|
||||
encryptionBigInteger.prototype.squareTo = bnpSquareTo;
|
||||
encryptionBigInteger.prototype.divRemTo = bnpDivRemTo;
|
||||
encryptionBigInteger.prototype.invDigit = bnpInvDigit;
|
||||
encryptionBigInteger.prototype.isEven = bnpIsEven;
|
||||
encryptionBigInteger.prototype.exp = bnpExp;
|
||||
|
||||
// public
|
||||
encryptionBigInteger.prototype.toString = bnToString;
|
||||
encryptionBigInteger.prototype.negate = bnNegate;
|
||||
encryptionBigInteger.prototype.abs = bnAbs;
|
||||
encryptionBigInteger.prototype.compareTo = bnCompareTo;
|
||||
encryptionBigInteger.prototype.bitLength = bnBitLength;
|
||||
encryptionBigInteger.prototype.mod = bnMod;
|
||||
encryptionBigInteger.prototype.modPowInt = bnModPowInt;
|
||||
|
||||
// "constants"
|
||||
encryptionBigInteger.ZERO = nbv(0);
|
||||
encryptionBigInteger.ONE = nbv(1);
|
656
js/spice-web-client/lib/jsbn2.js
Normal file
656
js/spice-web-client/lib/jsbn2.js
Normal file
@ -0,0 +1,656 @@
|
||||
// Copyright (c) 2005-2009 Tom Wu
|
||||
// All Rights Reserved.
|
||||
// See "LICENSE" for details.
|
||||
|
||||
// Extended JavaScript BN functions, required for RSA private ops.
|
||||
|
||||
// Version 1.1: new encryptionBigInteger("0", 10) returns "proper" zero
|
||||
// Version 1.2: square() API, isProbablePrime fix
|
||||
|
||||
// (public)
|
||||
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
|
||||
|
||||
// (public) return value as integer
|
||||
function bnIntValue() {
|
||||
if(this.s < 0) {
|
||||
if(this.t == 1) return this[0]-this.DV;
|
||||
else if(this.t == 0) return -1;
|
||||
}
|
||||
else if(this.t == 1) return this[0];
|
||||
else if(this.t == 0) return 0;
|
||||
// assumes 16 < DB < 32
|
||||
return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
|
||||
}
|
||||
|
||||
// (public) return value as byte
|
||||
function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }
|
||||
|
||||
// (public) return value as short (assumes DB>=16)
|
||||
function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; }
|
||||
|
||||
// (protected) return x s.t. r^x < DV
|
||||
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }
|
||||
|
||||
// (public) 0 if this == 0, 1 if this > 0
|
||||
function bnSigNum() {
|
||||
if(this.s < 0) return -1;
|
||||
else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
// (protected) convert to radix string
|
||||
function bnpToRadix(b) {
|
||||
if(b == null) b = 10;
|
||||
if(this.signum() == 0 || b < 2 || b > 36) return "0";
|
||||
var cs = this.chunkSize(b);
|
||||
var a = Math.pow(b,cs);
|
||||
var d = nbv(a), y = nbi(), z = nbi(), r = "";
|
||||
this.divRemTo(d,y,z);
|
||||
while(y.signum() > 0) {
|
||||
r = (a+z.intValue()).toString(b).substr(1) + r;
|
||||
y.divRemTo(d,y,z);
|
||||
}
|
||||
return z.intValue().toString(b) + r;
|
||||
}
|
||||
|
||||
// (protected) convert from radix string
|
||||
function bnpFromRadix(s,b) {
|
||||
this.fromInt(0);
|
||||
if(b == null) b = 10;
|
||||
var cs = this.chunkSize(b);
|
||||
var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
|
||||
for(var i = 0; i < s.length; ++i) {
|
||||
var x = intAt(s,i);
|
||||
if(x < 0) {
|
||||
if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
|
||||
continue;
|
||||
}
|
||||
w = b*w+x;
|
||||
if(++j >= cs) {
|
||||
this.dMultiply(d);
|
||||
this.dAddOffset(w,0);
|
||||
j = 0;
|
||||
w = 0;
|
||||
}
|
||||
}
|
||||
if(j > 0) {
|
||||
this.dMultiply(Math.pow(b,j));
|
||||
this.dAddOffset(w,0);
|
||||
}
|
||||
if(mi) encryptionBigInteger.ZERO.subTo(this,this);
|
||||
}
|
||||
|
||||
// (protected) alternate constructor
|
||||
function bnpFromNumber(a,b,c) {
|
||||
if("number" == typeof b) {
|
||||
// new encryptionBigInteger(int,int,RNG)
|
||||
if(a < 2) this.fromInt(1);
|
||||
else {
|
||||
this.fromNumber(a,c);
|
||||
if(!this.testBit(a-1)) // force MSB set
|
||||
this.bitwiseTo(encryptionBigInteger.ONE.shiftLeft(a-1),op_or,this);
|
||||
if(this.isEven()) this.dAddOffset(1,0); // force odd
|
||||
while(!this.isProbablePrime(b)) {
|
||||
this.dAddOffset(2,0);
|
||||
if(this.bitLength() > a) this.subTo(encryptionBigInteger.ONE.shiftLeft(a-1),this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// new encryptionBigInteger(int,RNG)
|
||||
var x = new Array(), t = a&7;
|
||||
x.length = (a>>3)+1;
|
||||
b.nextBytes(x);
|
||||
if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
|
||||
this.fromString(x,256);
|
||||
}
|
||||
}
|
||||
|
||||
// (public) convert to bigendian byte array
|
||||
function bnToByteArray() {
|
||||
var i = this.t, r = new Array();
|
||||
r[0] = this.s;
|
||||
var p = this.DB-(i*this.DB)%8, d, k = 0;
|
||||
if(i-- > 0) {
|
||||
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
|
||||
r[k++] = d|(this.s<<(this.DB-p));
|
||||
while(i >= 0) {
|
||||
if(p < 8) {
|
||||
d = (this[i]&((1<<p)-1))<<(8-p);
|
||||
d |= this[--i]>>(p+=this.DB-8);
|
||||
}
|
||||
else {
|
||||
d = (this[i]>>(p-=8))&0xff;
|
||||
if(p <= 0) { p += this.DB; --i; }
|
||||
}
|
||||
if((d&0x80) != 0) d |= -256;
|
||||
if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
|
||||
if(k > 0 || d != this.s) r[k++] = d;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function bnEquals(a) { return(this.compareTo(a)==0); }
|
||||
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
|
||||
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
|
||||
|
||||
// (protected) r = this op a (bitwise)
|
||||
function bnpBitwiseTo(a,op,r) {
|
||||
var i, f, m = Math.min(a.t,this.t);
|
||||
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
|
||||
if(a.t < this.t) {
|
||||
f = a.s&this.DM;
|
||||
for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
|
||||
r.t = this.t;
|
||||
}
|
||||
else {
|
||||
f = this.s&this.DM;
|
||||
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
|
||||
r.t = a.t;
|
||||
}
|
||||
r.s = op(this.s,a.s);
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (public) this & a
|
||||
function op_and(x,y) { return x&y; }
|
||||
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
|
||||
|
||||
// (public) this | a
|
||||
function op_or(x,y) { return x|y; }
|
||||
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
|
||||
|
||||
// (public) this ^ a
|
||||
function op_xor(x,y) { return x^y; }
|
||||
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
|
||||
|
||||
// (public) this & ~a
|
||||
function op_andnot(x,y) { return x&~y; }
|
||||
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
|
||||
|
||||
// (public) ~this
|
||||
function bnNot() {
|
||||
var r = nbi();
|
||||
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
|
||||
r.t = this.t;
|
||||
r.s = ~this.s;
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) this << n
|
||||
function bnShiftLeft(n) {
|
||||
var r = nbi();
|
||||
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) this >> n
|
||||
function bnShiftRight(n) {
|
||||
var r = nbi();
|
||||
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// return index of lowest 1-bit in x, x < 2^31
|
||||
function lbit(x) {
|
||||
if(x == 0) return -1;
|
||||
var r = 0;
|
||||
if((x&0xffff) == 0) { x >>= 16; r += 16; }
|
||||
if((x&0xff) == 0) { x >>= 8; r += 8; }
|
||||
if((x&0xf) == 0) { x >>= 4; r += 4; }
|
||||
if((x&3) == 0) { x >>= 2; r += 2; }
|
||||
if((x&1) == 0) ++r;
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) returns index of lowest 1-bit (or -1 if none)
|
||||
function bnGetLowestSetBit() {
|
||||
for(var i = 0; i < this.t; ++i)
|
||||
if(this[i] != 0) return i*this.DB+lbit(this[i]);
|
||||
if(this.s < 0) return this.t*this.DB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return number of 1 bits in x
|
||||
function cbit(x) {
|
||||
var r = 0;
|
||||
while(x != 0) { x &= x-1; ++r; }
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) return number of set bits
|
||||
function bnBitCount() {
|
||||
var r = 0, x = this.s&this.DM;
|
||||
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) true iff nth bit is set
|
||||
function bnTestBit(n) {
|
||||
var j = Math.floor(n/this.DB);
|
||||
if(j >= this.t) return(this.s!=0);
|
||||
return((this[j]&(1<<(n%this.DB)))!=0);
|
||||
}
|
||||
|
||||
// (protected) this op (1<<n)
|
||||
function bnpChangeBit(n,op) {
|
||||
var r = encryptionBigInteger.ONE.shiftLeft(n);
|
||||
this.bitwiseTo(r,op,r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) this | (1<<n)
|
||||
function bnSetBit(n) { return this.changeBit(n,op_or); }
|
||||
|
||||
// (public) this & ~(1<<n)
|
||||
function bnClearBit(n) { return this.changeBit(n,op_andnot); }
|
||||
|
||||
// (public) this ^ (1<<n)
|
||||
function bnFlipBit(n) { return this.changeBit(n,op_xor); }
|
||||
|
||||
// (protected) r = this + a
|
||||
function bnpAddTo(a,r) {
|
||||
var i = 0, c = 0, m = Math.min(a.t,this.t);
|
||||
while(i < m) {
|
||||
c += this[i]+a[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
if(a.t < this.t) {
|
||||
c += a.s;
|
||||
while(i < this.t) {
|
||||
c += this[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
c += this.s;
|
||||
}
|
||||
else {
|
||||
c += this.s;
|
||||
while(i < a.t) {
|
||||
c += a[i];
|
||||
r[i++] = c&this.DM;
|
||||
c >>= this.DB;
|
||||
}
|
||||
c += a.s;
|
||||
}
|
||||
r.s = (c<0)?-1:0;
|
||||
if(c > 0) r[i++] = c;
|
||||
else if(c < -1) r[i++] = this.DV+c;
|
||||
r.t = i;
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (public) this + a
|
||||
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
|
||||
|
||||
// (public) this - a
|
||||
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
|
||||
|
||||
// (public) this * a
|
||||
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
|
||||
|
||||
// (public) this^2
|
||||
function bnSquare() { var r = nbi(); this.squareTo(r); return r; }
|
||||
|
||||
// (public) this / a
|
||||
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
|
||||
|
||||
// (public) this % a
|
||||
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
|
||||
|
||||
// (public) [this/a,this%a]
|
||||
function bnDivideAndRemainder(a) {
|
||||
var q = nbi(), r = nbi();
|
||||
this.divRemTo(a,q,r);
|
||||
return new Array(q,r);
|
||||
}
|
||||
|
||||
// (protected) this *= n, this >= 0, 1 < n < DV
|
||||
function bnpDMultiply(n) {
|
||||
this[this.t] = this.am(0,n-1,this,0,0,this.t);
|
||||
++this.t;
|
||||
this.clamp();
|
||||
}
|
||||
|
||||
// (protected) this += n << w words, this >= 0
|
||||
function bnpDAddOffset(n,w) {
|
||||
if(n == 0) return;
|
||||
while(this.t <= w) this[this.t++] = 0;
|
||||
this[w] += n;
|
||||
while(this[w] >= this.DV) {
|
||||
this[w] -= this.DV;
|
||||
if(++w >= this.t) this[this.t++] = 0;
|
||||
++this[w];
|
||||
}
|
||||
}
|
||||
|
||||
// A "null" reducer
|
||||
function NullExp() {}
|
||||
function nNop(x) { return x; }
|
||||
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
|
||||
function nSqrTo(x,r) { x.squareTo(r); }
|
||||
|
||||
NullExp.prototype.convert = nNop;
|
||||
NullExp.prototype.revert = nNop;
|
||||
NullExp.prototype.mulTo = nMulTo;
|
||||
NullExp.prototype.sqrTo = nSqrTo;
|
||||
|
||||
// (public) this^e
|
||||
function bnPow(e) { return this.exp(e,new NullExp()); }
|
||||
|
||||
// (protected) r = lower n words of "this * a", a.t <= n
|
||||
// "this" should be the larger one if appropriate.
|
||||
function bnpMultiplyLowerTo(a,n,r) {
|
||||
var i = Math.min(this.t+a.t,n);
|
||||
r.s = 0; // assumes a,this >= 0
|
||||
r.t = i;
|
||||
while(i > 0) r[--i] = 0;
|
||||
var j;
|
||||
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
|
||||
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
|
||||
r.clamp();
|
||||
}
|
||||
|
||||
// (protected) r = "this * a" without lower n words, n > 0
|
||||
// "this" should be the larger one if appropriate.
|
||||
function bnpMultiplyUpperTo(a,n,r) {
|
||||
--n;
|
||||
var i = r.t = this.t+a.t-n;
|
||||
r.s = 0; // assumes a,this >= 0
|
||||
while(--i >= 0) r[i] = 0;
|
||||
for(i = Math.max(n-this.t,0); i < a.t; ++i)
|
||||
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
|
||||
r.clamp();
|
||||
r.drShiftTo(1,r);
|
||||
}
|
||||
|
||||
// Barrett modular reduction
|
||||
function Barrett(m) {
|
||||
// setup Barrett
|
||||
this.r2 = nbi();
|
||||
this.q3 = nbi();
|
||||
encryptionBigInteger.ONE.dlShiftTo(2*m.t,this.r2);
|
||||
this.mu = this.r2.divide(m);
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
function barrettConvert(x) {
|
||||
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
|
||||
else if(x.compareTo(this.m) < 0) return x;
|
||||
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
|
||||
}
|
||||
|
||||
function barrettRevert(x) { return x; }
|
||||
|
||||
// x = x mod m (HAC 14.42)
|
||||
function barrettReduce(x) {
|
||||
x.drShiftTo(this.m.t-1,this.r2);
|
||||
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
|
||||
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
|
||||
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
|
||||
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
|
||||
x.subTo(this.r2,x);
|
||||
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
|
||||
}
|
||||
|
||||
// r = x^2 mod m; x != r
|
||||
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
|
||||
|
||||
// r = x*y mod m; x,y != r
|
||||
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
|
||||
|
||||
Barrett.prototype.convert = barrettConvert;
|
||||
Barrett.prototype.revert = barrettRevert;
|
||||
Barrett.prototype.reduce = barrettReduce;
|
||||
Barrett.prototype.mulTo = barrettMulTo;
|
||||
Barrett.prototype.sqrTo = barrettSqrTo;
|
||||
|
||||
// (public) this^e % m (HAC 14.85)
|
||||
function bnModPow(e,m) {
|
||||
var i = e.bitLength(), k, r = nbv(1), z;
|
||||
if(i <= 0) return r;
|
||||
else if(i < 18) k = 1;
|
||||
else if(i < 48) k = 3;
|
||||
else if(i < 144) k = 4;
|
||||
else if(i < 768) k = 5;
|
||||
else k = 6;
|
||||
if(i < 8)
|
||||
z = new Classic(m);
|
||||
else if(m.isEven())
|
||||
z = new Barrett(m);
|
||||
else
|
||||
z = new Montgomery(m);
|
||||
|
||||
// precomputation
|
||||
var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
|
||||
g[1] = z.convert(this);
|
||||
if(k > 1) {
|
||||
var g2 = nbi();
|
||||
z.sqrTo(g[1],g2);
|
||||
while(n <= km) {
|
||||
g[n] = nbi();
|
||||
z.mulTo(g2,g[n-2],g[n]);
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
|
||||
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
|
||||
i = nbits(e[j])-1;
|
||||
while(j >= 0) {
|
||||
if(i >= k1) w = (e[j]>>(i-k1))&km;
|
||||
else {
|
||||
w = (e[j]&((1<<(i+1))-1))<<(k1-i);
|
||||
if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
|
||||
}
|
||||
|
||||
n = k;
|
||||
while((w&1) == 0) { w >>= 1; --n; }
|
||||
if((i -= n) < 0) { i += this.DB; --j; }
|
||||
if(is1) { // ret == 1, don't bother squaring or multiplying it
|
||||
g[w].copyTo(r);
|
||||
is1 = false;
|
||||
}
|
||||
else {
|
||||
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
|
||||
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
|
||||
z.mulTo(r2,g[w],r);
|
||||
}
|
||||
|
||||
while(j >= 0 && (e[j]&(1<<i)) == 0) {
|
||||
z.sqrTo(r,r2); t = r; r = r2; r2 = t;
|
||||
if(--i < 0) { i = this.DB-1; --j; }
|
||||
}
|
||||
}
|
||||
return z.revert(r);
|
||||
}
|
||||
|
||||
// (public) gcd(this,a) (HAC 14.54)
|
||||
function bnGCD(a) {
|
||||
var x = (this.s<0)?this.negate():this.clone();
|
||||
var y = (a.s<0)?a.negate():a.clone();
|
||||
if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
|
||||
var i = x.getLowestSetBit(), g = y.getLowestSetBit();
|
||||
if(g < 0) return x;
|
||||
if(i < g) g = i;
|
||||
if(g > 0) {
|
||||
x.rShiftTo(g,x);
|
||||
y.rShiftTo(g,y);
|
||||
}
|
||||
while(x.signum() > 0) {
|
||||
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
|
||||
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
|
||||
if(x.compareTo(y) >= 0) {
|
||||
x.subTo(y,x);
|
||||
x.rShiftTo(1,x);
|
||||
}
|
||||
else {
|
||||
y.subTo(x,y);
|
||||
y.rShiftTo(1,y);
|
||||
}
|
||||
}
|
||||
if(g > 0) y.lShiftTo(g,y);
|
||||
return y;
|
||||
}
|
||||
|
||||
// (protected) this % n, n < 2^26
|
||||
function bnpModInt(n) {
|
||||
if(n <= 0) return 0;
|
||||
var d = this.DV%n, r = (this.s<0)?n-1:0;
|
||||
if(this.t > 0)
|
||||
if(d == 0) r = this[0]%n;
|
||||
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
|
||||
return r;
|
||||
}
|
||||
|
||||
// (public) 1/this % m (HAC 14.61)
|
||||
function bnModInverse(m) {
|
||||
var ac = m.isEven();
|
||||
if((this.isEven() && ac) || m.signum() == 0) return encryptionBigInteger.ZERO;
|
||||
var u = m.clone(), v = this.clone();
|
||||
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
|
||||
while(u.signum() != 0) {
|
||||
while(u.isEven()) {
|
||||
u.rShiftTo(1,u);
|
||||
if(ac) {
|
||||
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
|
||||
a.rShiftTo(1,a);
|
||||
}
|
||||
else if(!b.isEven()) b.subTo(m,b);
|
||||
b.rShiftTo(1,b);
|
||||
}
|
||||
while(v.isEven()) {
|
||||
v.rShiftTo(1,v);
|
||||
if(ac) {
|
||||
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
|
||||
c.rShiftTo(1,c);
|
||||
}
|
||||
else if(!d.isEven()) d.subTo(m,d);
|
||||
d.rShiftTo(1,d);
|
||||
}
|
||||
if(u.compareTo(v) >= 0) {
|
||||
u.subTo(v,u);
|
||||
if(ac) a.subTo(c,a);
|
||||
b.subTo(d,b);
|
||||
}
|
||||
else {
|
||||
v.subTo(u,v);
|
||||
if(ac) c.subTo(a,c);
|
||||
d.subTo(b,d);
|
||||
}
|
||||
}
|
||||
if(v.compareTo(encryptionBigInteger.ONE) != 0) return encryptionBigInteger.ZERO;
|
||||
if(d.compareTo(m) >= 0) return d.subtract(m);
|
||||
if(d.signum() < 0) d.addTo(m,d); else return d;
|
||||
if(d.signum() < 0) return d.add(m); else return d;
|
||||
}
|
||||
|
||||
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
|
||||
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
|
||||
|
||||
// (public) test primality with certainty >= 1-.5^t
|
||||
function bnIsProbablePrime(t) {
|
||||
var i, x = this.abs();
|
||||
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
|
||||
for(i = 0; i < lowprimes.length; ++i)
|
||||
if(x[0] == lowprimes[i]) return true;
|
||||
return false;
|
||||
}
|
||||
if(x.isEven()) return false;
|
||||
i = 1;
|
||||
while(i < lowprimes.length) {
|
||||
var m = lowprimes[i], j = i+1;
|
||||
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
|
||||
m = x.modInt(m);
|
||||
while(i < j) if(m%lowprimes[i++] == 0) return false;
|
||||
}
|
||||
return x.millerRabin(t);
|
||||
}
|
||||
|
||||
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
|
||||
function bnpMillerRabin(t) {
|
||||
var n1 = this.subtract(encryptionBigInteger.ONE);
|
||||
var k = n1.getLowestSetBit();
|
||||
if(k <= 0) return false;
|
||||
var r = n1.shiftRight(k);
|
||||
t = (t+1)>>1;
|
||||
if(t > lowprimes.length) t = lowprimes.length;
|
||||
var a = nbi();
|
||||
for(var i = 0; i < t; ++i) {
|
||||
//Pick bases at random, instead of starting at 2
|
||||
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
|
||||
var y = a.modPow(r,this);
|
||||
if(y.compareTo(encryptionBigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
|
||||
var j = 1;
|
||||
while(j++ < k && y.compareTo(n1) != 0) {
|
||||
y = y.modPowInt(2,this);
|
||||
if(y.compareTo(encryptionBigInteger.ONE) == 0) return false;
|
||||
}
|
||||
if(y.compareTo(n1) != 0) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// protected
|
||||
encryptionBigInteger.prototype.chunkSize = bnpChunkSize;
|
||||
encryptionBigInteger.prototype.toRadix = bnpToRadix;
|
||||
encryptionBigInteger.prototype.fromRadix = bnpFromRadix;
|
||||
encryptionBigInteger.prototype.fromNumber = bnpFromNumber;
|
||||
encryptionBigInteger.prototype.bitwiseTo = bnpBitwiseTo;
|
||||
encryptionBigInteger.prototype.changeBit = bnpChangeBit;
|
||||
encryptionBigInteger.prototype.addTo = bnpAddTo;
|
||||
encryptionBigInteger.prototype.dMultiply = bnpDMultiply;
|
||||
encryptionBigInteger.prototype.dAddOffset = bnpDAddOffset;
|
||||
encryptionBigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
|
||||
encryptionBigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
|
||||
encryptionBigInteger.prototype.modInt = bnpModInt;
|
||||
encryptionBigInteger.prototype.millerRabin = bnpMillerRabin;
|
||||
|
||||
// public
|
||||
encryptionBigInteger.prototype.clone = bnClone;
|
||||
encryptionBigInteger.prototype.intValue = bnIntValue;
|
||||
encryptionBigInteger.prototype.byteValue = bnByteValue;
|
||||
encryptionBigInteger.prototype.shortValue = bnShortValue;
|
||||
encryptionBigInteger.prototype.signum = bnSigNum;
|
||||
encryptionBigInteger.prototype.toByteArray = bnToByteArray;
|
||||
encryptionBigInteger.prototype.equals = bnEquals;
|
||||
encryptionBigInteger.prototype.min = bnMin;
|
||||
encryptionBigInteger.prototype.max = bnMax;
|
||||
encryptionBigInteger.prototype.and = bnAnd;
|
||||
encryptionBigInteger.prototype.or = bnOr;
|
||||
encryptionBigInteger.prototype.xor = bnXor;
|
||||
encryptionBigInteger.prototype.andNot = bnAndNot;
|
||||
encryptionBigInteger.prototype.not = bnNot;
|
||||
encryptionBigInteger.prototype.shiftLeft = bnShiftLeft;
|
||||
encryptionBigInteger.prototype.shiftRight = bnShiftRight;
|
||||
encryptionBigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
|
||||
encryptionBigInteger.prototype.bitCount = bnBitCount;
|
||||
encryptionBigInteger.prototype.testBit = bnTestBit;
|
||||
encryptionBigInteger.prototype.setBit = bnSetBit;
|
||||
encryptionBigInteger.prototype.clearBit = bnClearBit;
|
||||
encryptionBigInteger.prototype.flipBit = bnFlipBit;
|
||||
encryptionBigInteger.prototype.add = bnAdd;
|
||||
encryptionBigInteger.prototype.subtract = bnSubtract;
|
||||
encryptionBigInteger.prototype.multiply = bnMultiply;
|
||||
encryptionBigInteger.prototype.divide = bnDivide;
|
||||
encryptionBigInteger.prototype.remainder = bnRemainder;
|
||||
encryptionBigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
|
||||
encryptionBigInteger.prototype.modPow = bnModPow;
|
||||
encryptionBigInteger.prototype.modInverse = bnModInverse;
|
||||
encryptionBigInteger.prototype.pow = bnPow;
|
||||
encryptionBigInteger.prototype.gcd = bnGCD;
|
||||
encryptionBigInteger.prototype.isProbablePrime = bnIsProbablePrime;
|
||||
|
||||
// JSBN-specific extension
|
||||
encryptionBigInteger.prototype.square = bnSquare;
|
||||
|
||||
// encryptionBigInteger interfaces not implemented in jsbn:
|
||||
|
||||
// encryptionBigInteger(int signum, byte[] magnitude)
|
||||
// double doubleValue()
|
||||
// float floatValue()
|
||||
// int hashCode()
|
||||
// long longValue()
|
||||
// static encryptionBigInteger valueOf(long val)
|
20
js/spice-web-client/lib/modernizr.js
Normal file
20
js/spice-web-client/lib/modernizr.js
Normal file
File diff suppressed because one or more lines are too long
3225
js/spice-web-client/lib/pixastic.js
Normal file
3225
js/spice-web-client/lib/pixastic.js
Normal file
File diff suppressed because it is too large
Load Diff
775
js/spice-web-client/lib/prettyprint.js
Normal file
775
js/spice-web-client/lib/prettyprint.js
Normal file
@ -0,0 +1,775 @@
|
||||
/*
|
||||
Copyright (c) 2009 James Padolsey. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY James Padolsey ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL James Padolsey OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are
|
||||
those of the authors and should not be interpreted as representing official
|
||||
policies, either expressed or implied, of James Padolsey.
|
||||
|
||||
AUTHOR James Padolsey (http://james.padolsey.com)
|
||||
VERSION 1.03.0
|
||||
UPDATED 29-10-2011
|
||||
CONTRIBUTORS
|
||||
David Waller
|
||||
Benjamin Drucker
|
||||
|
||||
*/
|
||||
|
||||
var prettyPrint = (function(){
|
||||
|
||||
/* These "util" functions are not part of the core
|
||||
functionality but are all necessary - mostly DOM helpers */
|
||||
|
||||
var util = {
|
||||
|
||||
el: function(type, attrs) {
|
||||
|
||||
/* Create new element */
|
||||
var el = document.createElement(type), attr;
|
||||
|
||||
/*Copy to single object */
|
||||
attrs = util.merge({}, attrs);
|
||||
|
||||
/* Add attributes to el */
|
||||
if (attrs && attrs.style) {
|
||||
var styles = attrs.style;
|
||||
util.applyCSS( el, attrs.style );
|
||||
delete attrs.style;
|
||||
}
|
||||
for (attr in attrs) {
|
||||
if (attrs.hasOwnProperty(attr)) {
|
||||
el[attr] = attrs[attr];
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
|
||||
},
|
||||
|
||||
applyCSS: function(el, styles) {
|
||||
/* Applies CSS to a single element */
|
||||
for (var prop in styles) {
|
||||
if (styles.hasOwnProperty(prop)) {
|
||||
try{
|
||||
/* Yes, IE6 SUCKS! */
|
||||
el.style[prop] = styles[prop];
|
||||
}catch(e){}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
txt: function(t) {
|
||||
/* Create text node */
|
||||
return document.createTextNode(t);
|
||||
},
|
||||
|
||||
row: function(cells, type, cellType) {
|
||||
|
||||
/* Creates new <tr> */
|
||||
cellType = cellType || 'td';
|
||||
|
||||
/* colSpan is calculated by length of null items in array */
|
||||
var colSpan = util.count(cells, null) + 1,
|
||||
tr = util.el('tr'), td,
|
||||
attrs = {
|
||||
style: util.getStyles(cellType, type),
|
||||
colSpan: colSpan,
|
||||
onmouseover: function() {
|
||||
var tds = this.parentNode.childNodes;
|
||||
util.forEach(tds, function(cell){
|
||||
if (cell.nodeName.toLowerCase() !== 'td') { return; }
|
||||
util.applyCSS(cell, util.getStyles('td_hover', type));
|
||||
});
|
||||
},
|
||||
onmouseout: function() {
|
||||
var tds = this.parentNode.childNodes;
|
||||
util.forEach(tds, function(cell){
|
||||
if (cell.nodeName.toLowerCase() !== 'td') { return; }
|
||||
util.applyCSS(cell, util.getStyles('td', type));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
util.forEach(cells, function(cell){
|
||||
|
||||
if (cell === null) { return; }
|
||||
/* Default cell type is <td> */
|
||||
td = util.el(cellType, attrs);
|
||||
|
||||
if (cell.nodeType) {
|
||||
/* IsDomElement */
|
||||
td.appendChild(cell);
|
||||
} else {
|
||||
/* IsString */
|
||||
td.innerHTML = util.shorten(cell.toString());
|
||||
}
|
||||
|
||||
tr.appendChild(td);
|
||||
});
|
||||
|
||||
return tr;
|
||||
},
|
||||
|
||||
hRow: function(cells, type){
|
||||
/* Return new <th> */
|
||||
return util.row(cells, type, 'th');
|
||||
},
|
||||
|
||||
table: function(headings, type){
|
||||
|
||||
headings = headings || [];
|
||||
|
||||
/* Creates new table: */
|
||||
var attrs = {
|
||||
thead: {
|
||||
style:util.getStyles('thead',type)
|
||||
},
|
||||
tbody: {
|
||||
style:util.getStyles('tbody',type)
|
||||
},
|
||||
table: {
|
||||
style:util.getStyles('table',type)
|
||||
}
|
||||
},
|
||||
tbl = util.el('table', attrs.table),
|
||||
thead = util.el('thead', attrs.thead),
|
||||
tbody = util.el('tbody', attrs.tbody);
|
||||
|
||||
if (headings.length) {
|
||||
tbl.appendChild(thead);
|
||||
thead.appendChild( util.hRow(headings, type) );
|
||||
}
|
||||
tbl.appendChild(tbody);
|
||||
|
||||
return {
|
||||
/* Facade for dealing with table/tbody
|
||||
Actual table node is this.node: */
|
||||
node: tbl,
|
||||
tbody: tbody,
|
||||
thead: thead,
|
||||
appendChild: function(node) {
|
||||
this.tbody.appendChild(node);
|
||||
},
|
||||
addRow: function(cells, _type, cellType){
|
||||
this.appendChild(util.row.call(util, cells, (_type || type), cellType));
|
||||
return this;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
shorten: function(str) {
|
||||
var max = 40;
|
||||
str = str.replace(/^\s\s*|\s\s*$|\n/g,'');
|
||||
return str.length > max ? (str.substring(0, max-1) + '...') : str;
|
||||
},
|
||||
|
||||
htmlentities: function(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
},
|
||||
|
||||
merge: function(target, source) {
|
||||
|
||||
/* Merges two (or more) objects,
|
||||
giving the last one precedence */
|
||||
|
||||
if ( typeof target !== 'object' ) {
|
||||
target = {};
|
||||
}
|
||||
|
||||
for (var property in source) {
|
||||
|
||||
if ( source.hasOwnProperty(property) ) {
|
||||
|
||||
var sourceProperty = source[ property ];
|
||||
|
||||
if ( typeof sourceProperty === 'object' ) {
|
||||
target[ property ] = util.merge( target[ property ], sourceProperty );
|
||||
continue;
|
||||
}
|
||||
|
||||
target[ property ] = sourceProperty;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (var a = 2, l = arguments.length; a < l; a++) {
|
||||
util.merge(target, arguments[a]);
|
||||
}
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
count: function(arr, item) {
|
||||
var count = 0;
|
||||
for (var i = 0, l = arr.length; i< l; i++) {
|
||||
if (arr[i] === item) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
},
|
||||
|
||||
thead: function(tbl) {
|
||||
return tbl.getElementsByTagName('thead')[0];
|
||||
},
|
||||
|
||||
forEach: function(arr, max, fn) {
|
||||
|
||||
if (!fn) {
|
||||
fn = max;
|
||||
}
|
||||
|
||||
/* Helper: iteration */
|
||||
var len = arr.length,
|
||||
index = -1;
|
||||
|
||||
while (++index < len) {
|
||||
if(fn( arr[index], index, arr ) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
type: function(v){
|
||||
try {
|
||||
/* Returns type, e.g. "string", "number", "array" etc.
|
||||
Note, this is only used for precise typing. */
|
||||
if (v === null) { return 'null'; }
|
||||
if (v === undefined) { return 'undefined'; }
|
||||
var oType = Object.prototype.toString.call(v).match(/\s(.+?)\]/)[1].toLowerCase();
|
||||
if (v.nodeType) {
|
||||
if (v.nodeType === 1) {
|
||||
return 'domelement';
|
||||
}
|
||||
return 'domnode';
|
||||
}
|
||||
if (/^(string|number|array|regexp|function|date|boolean)$/.test(oType)) {
|
||||
return oType;
|
||||
}
|
||||
if (typeof v === 'object') {
|
||||
return v.jquery && typeof v.jquery === 'string' ? 'jquery' : 'object';
|
||||
}
|
||||
if (v === window || v === document) {
|
||||
return 'object';
|
||||
}
|
||||
return 'default';
|
||||
} catch(e) {
|
||||
return 'default';
|
||||
}
|
||||
},
|
||||
|
||||
within: function(ref) {
|
||||
/* Check existence of a val within an object
|
||||
RETURNS KEY */
|
||||
return {
|
||||
is: function(o) {
|
||||
for (var i in ref) {
|
||||
if (ref[i] === o) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
common: {
|
||||
circRef: function(obj, key, settings) {
|
||||
return util.expander(
|
||||
'[POINTS BACK TO <strong>' + (key) + '</strong>]',
|
||||
'Click to show this item anyway',
|
||||
function() {
|
||||
this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
|
||||
}
|
||||
);
|
||||
},
|
||||
depthReached: function(obj, settings) {
|
||||
return util.expander(
|
||||
'[DEPTH REACHED]',
|
||||
'Click to show this item anyway',
|
||||
function() {
|
||||
try {
|
||||
this.parentNode.appendChild( prettyPrintThis(obj,{maxDepth:1}) );
|
||||
} catch(e) {
|
||||
this.parentNode.appendChild(
|
||||
util.table(['ERROR OCCURED DURING OBJECT RETRIEVAL'],'error').addRow([e.message]).node
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
getStyles: function(el, type) {
|
||||
type = prettyPrintThis.settings.styles[type] || {};
|
||||
return util.merge(
|
||||
{}, prettyPrintThis.settings.styles['default'][el], type[el]
|
||||
);
|
||||
},
|
||||
|
||||
expander: function(text, title, clickFn) {
|
||||
return util.el('a', {
|
||||
innerHTML: util.shorten(text) + ' <b style="visibility:hidden;">[+]</b>',
|
||||
title: title,
|
||||
onmouseover: function() {
|
||||
this.getElementsByTagName('b')[0].style.visibility = 'visible';
|
||||
},
|
||||
onmouseout: function() {
|
||||
this.getElementsByTagName('b')[0].style.visibility = 'hidden';
|
||||
},
|
||||
onclick: function() {
|
||||
this.style.display = 'none';
|
||||
clickFn.call(this);
|
||||
return false;
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer'
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stringify: function(obj) {
|
||||
|
||||
/* Bit of an ugly duckling!
|
||||
- This fn returns an ATTEMPT at converting an object/array/anyType
|
||||
into a string, kinda like a JSON-deParser
|
||||
- This is used for when |settings.expanded === false| */
|
||||
|
||||
var type = util.type(obj),
|
||||
str, first = true;
|
||||
if ( type === 'array' ) {
|
||||
str = '[';
|
||||
util.forEach(obj, function(item,i){
|
||||
str += (i===0?'':', ') + util.stringify(item);
|
||||
});
|
||||
return str + ']';
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
str = '{';
|
||||
for (var i in obj){
|
||||
if (obj.hasOwnProperty(i)) {
|
||||
str += (first?'':', ') + i + ':' + util.stringify(obj[i]);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
return str + '}';
|
||||
}
|
||||
if (type === 'regexp') {
|
||||
return '/' + obj.source + '/';
|
||||
}
|
||||
if (type === 'string') {
|
||||
return '"' + obj.replace(/"/g,'\\"') + '"';
|
||||
}
|
||||
return obj.toString();
|
||||
},
|
||||
|
||||
headerGradient: (function(){
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
if (!canvas.getContext) { return ''; }
|
||||
var cx = canvas.getContext('2d');
|
||||
canvas.height = 30;
|
||||
canvas.width = 1;
|
||||
|
||||
var linearGrad = cx.createLinearGradient(0,0,0,30);
|
||||
linearGrad.addColorStop(0,'rgba(0,0,0,0)');
|
||||
linearGrad.addColorStop(1,'rgba(0,0,0,0.25)');
|
||||
|
||||
cx.fillStyle = linearGrad;
|
||||
cx.fillRect(0,0,1,30);
|
||||
|
||||
var dataURL = canvas.toDataURL && canvas.toDataURL();
|
||||
return 'url(' + (dataURL || '') + ')';
|
||||
|
||||
})()
|
||||
|
||||
};
|
||||
|
||||
// Main..
|
||||
var prettyPrintThis = function(obj, options) {
|
||||
|
||||
/*
|
||||
* obj :: Object to be printed
|
||||
* options :: Options (merged with config)
|
||||
*/
|
||||
|
||||
options = options || {};
|
||||
|
||||
var settings = util.merge( {}, prettyPrintThis.config, options ),
|
||||
container = util.el('div'),
|
||||
config = prettyPrintThis.config,
|
||||
currentDepth = 0,
|
||||
stack = {},
|
||||
hasRunOnce = false;
|
||||
|
||||
/* Expose per-call settings.
|
||||
Note: "config" is overwritten (where necessary) by options/"settings"
|
||||
So, if you need to access/change *DEFAULT* settings then go via ".config" */
|
||||
prettyPrintThis.settings = settings;
|
||||
|
||||
var typeDealer = {
|
||||
string : function(item){
|
||||
return util.txt('"' + util.shorten(item.replace(/"/g,'\\"')) + '"');
|
||||
},
|
||||
number : function(item) {
|
||||
return util.txt(item);
|
||||
},
|
||||
regexp : function(item) {
|
||||
|
||||
var miniTable = util.table(['RegExp',null], 'regexp');
|
||||
var flags = util.table();
|
||||
var span = util.expander(
|
||||
'/' + item.source + '/',
|
||||
'Click to show more',
|
||||
function() {
|
||||
this.parentNode.appendChild(miniTable.node);
|
||||
}
|
||||
);
|
||||
|
||||
flags
|
||||
.addRow(['g', item.global])
|
||||
.addRow(['i', item.ignoreCase])
|
||||
.addRow(['m', item.multiline]);
|
||||
|
||||
miniTable
|
||||
.addRow(['source', '/' + item.source + '/'])
|
||||
.addRow(['flags', flags.node])
|
||||
.addRow(['lastIndex', item.lastIndex]);
|
||||
|
||||
return settings.expanded ? miniTable.node : span;
|
||||
},
|
||||
domelement : function(element, depth) {
|
||||
|
||||
var miniTable = util.table(['DOMElement',null], 'domelement'),
|
||||
props = ['id', 'className', 'innerHTML', 'src', 'href'], elname = element.nodeName || '';
|
||||
|
||||
miniTable.addRow(['tag', '<' + elname.toLowerCase() + '>']);
|
||||
|
||||
util.forEach(props, function(prop){
|
||||
if ( element[prop] ) {
|
||||
miniTable.addRow([ prop, util.htmlentities(element[prop]) ]);
|
||||
}
|
||||
});
|
||||
|
||||
return settings.expanded ? miniTable.node : util.expander(
|
||||
'DOMElement (' + elname.toLowerCase() + ')',
|
||||
'Click to show more',
|
||||
function() {
|
||||
this.parentNode.appendChild(miniTable.node);
|
||||
}
|
||||
);
|
||||
},
|
||||
domnode : function(node){
|
||||
|
||||
/* Deals with all DOMNodes that aren't elements (nodeType !== 1) */
|
||||
var miniTable = util.table(['DOMNode',null], 'domelement'),
|
||||
data = util.htmlentities( (node.data || 'UNDEFINED').replace(/\n/g,'\\n') );
|
||||
miniTable
|
||||
.addRow(['nodeType', node.nodeType + ' (' + node.nodeName + ')'])
|
||||
.addRow(['data', data]);
|
||||
|
||||
return settings.expanded ? miniTable.node : util.expander(
|
||||
'DOMNode',
|
||||
'Click to show more',
|
||||
function() {
|
||||
this.parentNode.appendChild(miniTable.node);
|
||||
}
|
||||
);
|
||||
},
|
||||
jquery : function(obj, depth, key) {
|
||||
return typeDealer['array'](obj, depth, key, true);
|
||||
},
|
||||
object : function(obj, depth, key) {
|
||||
|
||||
/* Checking depth + circular refs */
|
||||
/* Note, check for circular refs before depth; just makes more sense */
|
||||
var stackKey = util.within(stack).is(obj);
|
||||
if ( stackKey ) {
|
||||
return util.common.circRef(obj, stackKey, settings);
|
||||
}
|
||||
stack[key||'TOP'] = obj;
|
||||
if (depth === settings.maxDepth) {
|
||||
return util.common.depthReached(obj, settings);
|
||||
}
|
||||
|
||||
var table = util.table(['Object', null],'object'),
|
||||
isEmpty = true;
|
||||
|
||||
for (var i in obj) {
|
||||
if (!obj.hasOwnProperty || obj.hasOwnProperty(i)) {
|
||||
var item = obj[i],
|
||||
type = util.type(item);
|
||||
isEmpty = false;
|
||||
try {
|
||||
table.addRow([i, typeDealer[ type ](item, depth+1, i)], type);
|
||||
} catch(e) {
|
||||
/* Security errors are thrown on certain Window/DOM properties */
|
||||
if (window.console && window.console.log) {
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
table.addRow(['<small>[empty]</small>']);
|
||||
} else {
|
||||
table.thead.appendChild(
|
||||
util.hRow(['key','value'], 'colHeader')
|
||||
);
|
||||
}
|
||||
|
||||
var ret = (settings.expanded || hasRunOnce) ? table.node : util.expander(
|
||||
util.stringify(obj),
|
||||
'Click to show more',
|
||||
function() {
|
||||
this.parentNode.appendChild(table.node);
|
||||
}
|
||||
);
|
||||
|
||||
hasRunOnce = true;
|
||||
|
||||
return ret;
|
||||
|
||||
},
|
||||
array : function(arr, depth, key, jquery) {
|
||||
|
||||
/* Checking depth + circular refs */
|
||||
/* Note, check for circular refs before depth; just makes more sense */
|
||||
var stackKey = util.within(stack).is(arr);
|
||||
if ( stackKey ) {
|
||||
return util.common.circRef(arr, stackKey);
|
||||
}
|
||||
stack[key||'TOP'] = arr;
|
||||
if (depth === settings.maxDepth) {
|
||||
return util.common.depthReached(arr);
|
||||
}
|
||||
|
||||
/* Accepts a table and modifies it */
|
||||
var me = jquery ? 'jQuery' : 'Array', table = util.table([me + '(' + arr.length + ')', null], jquery ? 'jquery' : me.toLowerCase()),
|
||||
isEmpty = true,
|
||||
count = 0;
|
||||
|
||||
if (jquery){
|
||||
table.addRow(['selector',arr.selector]);
|
||||
}
|
||||
|
||||
util.forEach(arr, function(item,i){
|
||||
if (settings.maxArray >= 0 && ++count > settings.maxArray) {
|
||||
table.addRow([
|
||||
i + '..' + (arr.length-1),
|
||||
typeDealer[ util.type(item) ]('...', depth+1, i)
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
isEmpty = false;
|
||||
table.addRow([i, typeDealer[ util.type(item) ](item, depth+1, i)]);
|
||||
});
|
||||
|
||||
if (!jquery){
|
||||
if (isEmpty) {
|
||||
table.addRow(['<small>[empty]</small>']);
|
||||
} else {
|
||||
table.thead.appendChild( util.hRow(['index','value'], 'colHeader') );
|
||||
}
|
||||
}
|
||||
|
||||
return settings.expanded ? table.node : util.expander(
|
||||
util.stringify(arr),
|
||||
'Click to show more',
|
||||
function() {
|
||||
this.parentNode.appendChild(table.node);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
'function' : function(fn, depth, key) {
|
||||
|
||||
/* Checking JUST circular refs */
|
||||
var stackKey = util.within(stack).is(fn);
|
||||
if ( stackKey ) { return util.common.circRef(fn, stackKey); }
|
||||
stack[key||'TOP'] = fn;
|
||||
|
||||
var miniTable = util.table(['Function',null], 'function'),
|
||||
argsTable = util.table(['Arguments']),
|
||||
args = fn.toString().match(/\((.+?)\)/),
|
||||
body = fn.toString().match(/\(.*?\)\s+?\{?([\S\s]+)/)[1].replace(/\}?$/,'');
|
||||
|
||||
miniTable
|
||||
.addRow(['arguments', args ? args[1].replace(/[^\w_,\s]/g,'') : '<small>[none/native]</small>'])
|
||||
.addRow(['body', body]);
|
||||
|
||||
return settings.expanded ? miniTable.node : util.expander(
|
||||
'function(){...}',
|
||||
'Click to see more about this function.',
|
||||
function(){
|
||||
this.parentNode.appendChild(miniTable.node);
|
||||
}
|
||||
);
|
||||
},
|
||||
'date' : function(date) {
|
||||
|
||||
var miniTable = util.table(['Date',null], 'date'),
|
||||
sDate = date.toString().split(/\s/);
|
||||
|
||||
/* TODO: Make this work well in IE! */
|
||||
miniTable
|
||||
.addRow(['Time', sDate[4]])
|
||||
.addRow(['Date', sDate.slice(0,4).join('-')]);
|
||||
|
||||
return settings.expanded ? miniTable.node : util.expander(
|
||||
'Date (timestamp): ' + (+date),
|
||||
'Click to see a little more info about this date',
|
||||
function() {
|
||||
this.parentNode.appendChild(miniTable.node);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
'boolean' : function(bool) {
|
||||
return util.txt( bool.toString().toUpperCase() );
|
||||
},
|
||||
'undefined' : function() {
|
||||
return util.txt('UNDEFINED');
|
||||
},
|
||||
'null' : function() {
|
||||
return util.txt('NULL');
|
||||
},
|
||||
'default' : function() {
|
||||
/* When a type cannot be found */
|
||||
return util.txt('prettyPrint: TypeNotFound Error');
|
||||
}
|
||||
};
|
||||
|
||||
container.appendChild( typeDealer[ (settings.forceObject) ? 'object' : util.type(obj) ](obj, currentDepth) );
|
||||
|
||||
return container;
|
||||
|
||||
};
|
||||
|
||||
/* Configuration */
|
||||
|
||||
/* All items can be overwridden by passing an
|
||||
"options" object when calling prettyPrint */
|
||||
prettyPrintThis.config = {
|
||||
|
||||
/* Try setting this to false to save space */
|
||||
expanded: true,
|
||||
|
||||
forceObject: false,
|
||||
maxDepth: 3,
|
||||
maxArray: -1, // default is unlimited
|
||||
styles: {
|
||||
array: {
|
||||
th: {
|
||||
backgroundColor: '#6DBD2A',
|
||||
color: 'white'
|
||||
}
|
||||
},
|
||||
'function': {
|
||||
th: {
|
||||
backgroundColor: '#D82525'
|
||||
}
|
||||
},
|
||||
regexp: {
|
||||
th: {
|
||||
backgroundColor: '#E2F3FB',
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
object: {
|
||||
th: {
|
||||
backgroundColor: '#1F96CF'
|
||||
}
|
||||
},
|
||||
jquery : {
|
||||
th: {
|
||||
backgroundColor: '#FBF315'
|
||||
}
|
||||
},
|
||||
error: {
|
||||
th: {
|
||||
backgroundColor: 'red',
|
||||
color: 'yellow'
|
||||
}
|
||||
},
|
||||
domelement: {
|
||||
th: {
|
||||
backgroundColor: '#F3801E'
|
||||
}
|
||||
},
|
||||
date: {
|
||||
th: {
|
||||
backgroundColor: '#A725D8'
|
||||
}
|
||||
},
|
||||
colHeader: {
|
||||
th: {
|
||||
backgroundColor: '#EEE',
|
||||
color: '#000',
|
||||
textTransform: 'uppercase'
|
||||
}
|
||||
},
|
||||
'default': {
|
||||
table: {
|
||||
borderCollapse: 'collapse',
|
||||
width: '100%'
|
||||
},
|
||||
td: {
|
||||
padding: '5px',
|
||||
fontSize: '12px',
|
||||
backgroundColor: '#FFF',
|
||||
color: '#222',
|
||||
border: '1px solid #000',
|
||||
verticalAlign: 'top',
|
||||
fontFamily: '"Consolas","Lucida Console",Courier,mono',
|
||||
whiteSpace: 'nowrap'
|
||||
},
|
||||
td_hover: {
|
||||
/* Styles defined here will apply to all tr:hover > td,
|
||||
- Be aware that "inheritable" properties (e.g. fontWeight) WILL BE INHERITED */
|
||||
},
|
||||
th: {
|
||||
padding: '5px',
|
||||
fontSize: '12px',
|
||||
backgroundColor: '#222',
|
||||
color: '#EEE',
|
||||
textAlign: 'left',
|
||||
border: '1px solid #000',
|
||||
verticalAlign: 'top',
|
||||
fontFamily: '"Consolas","Lucida Console",Courier,mono',
|
||||
backgroundImage: util.headerGradient,
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return prettyPrintThis;
|
||||
|
||||
})();
|
45
js/spice-web-client/lib/prng4.js
Normal file
45
js/spice-web-client/lib/prng4.js
Normal file
@ -0,0 +1,45 @@
|
||||
// prng4.js - uses Arcfour as a PRNG
|
||||
|
||||
function Arcfour() {
|
||||
this.i = 0;
|
||||
this.j = 0;
|
||||
this.S = new Array();
|
||||
}
|
||||
|
||||
// Initialize arcfour context from key, an array of ints, each from [0..255]
|
||||
function ARC4init(key) {
|
||||
var i, j, t;
|
||||
for(i = 0; i < 256; ++i)
|
||||
this.S[i] = i;
|
||||
j = 0;
|
||||
for(i = 0; i < 256; ++i) {
|
||||
j = (j + this.S[i] + key[i % key.length]) & 255;
|
||||
t = this.S[i];
|
||||
this.S[i] = this.S[j];
|
||||
this.S[j] = t;
|
||||
}
|
||||
this.i = 0;
|
||||
this.j = 0;
|
||||
}
|
||||
|
||||
function ARC4next() {
|
||||
var t;
|
||||
this.i = (this.i + 1) & 255;
|
||||
this.j = (this.j + this.S[this.i]) & 255;
|
||||
t = this.S[this.i];
|
||||
this.S[this.i] = this.S[this.j];
|
||||
this.S[this.j] = t;
|
||||
return this.S[(t + this.S[this.i]) & 255];
|
||||
}
|
||||
|
||||
Arcfour.prototype.init = ARC4init;
|
||||
Arcfour.prototype.next = ARC4next;
|
||||
|
||||
// Plug in your RNG constructor here
|
||||
function prng_newstate() {
|
||||
return new Arcfour();
|
||||
}
|
||||
|
||||
// Pool size must be a multiple of 4 and greater than 32.
|
||||
// An array of bytes the size of the pool will be passed to init()
|
||||
var rng_psize = 256;
|
272
js/spice-web-client/lib/queue.js
Normal file
272
js/spice-web-client/lib/queue.js
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
Uint8Array.prototype.toJSArray = function() {
|
||||
if(this.length == 1) {
|
||||
return [this[0]];
|
||||
}
|
||||
|
||||
var len = this.length;
|
||||
var arr = new Array(len);
|
||||
|
||||
for(var i=0;i<len;i++) {
|
||||
arr[i] = this[i];
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
wdi.FixedQueue = $.spcExtend(wdi.DomainObject, {
|
||||
q: null,
|
||||
size: 1024*1024*10,
|
||||
grow: 1024*1024*10,
|
||||
woffset: 0,
|
||||
roffset: 0,
|
||||
|
||||
init: function(c) {
|
||||
this.q = new Uint8Array(this.size);
|
||||
},
|
||||
|
||||
setData: function(q) {
|
||||
this.woffset = q.length;
|
||||
this.roffset = 0;
|
||||
this.q.set(q);
|
||||
},
|
||||
|
||||
shift: function(elements) {
|
||||
if(this.roffset + elements > this.woffset) {
|
||||
throw "Not enough queue to read";
|
||||
}
|
||||
var toreturn = this.q.subarray(this.roffset, this.roffset + elements);
|
||||
this.roffset = this.roffset + elements;
|
||||
if(this.woffset == this.roffset) {
|
||||
this.woffset = 0;
|
||||
this.roffset = 0;
|
||||
}
|
||||
return toreturn;
|
||||
},
|
||||
|
||||
push: function(collection) {
|
||||
if(this.woffset + collection.byteLength > this.size) {
|
||||
//we need to make the queue bigger...
|
||||
var oldq = this.q;
|
||||
this.size += this.grow;
|
||||
this.q = new Uint8Array(this.size);
|
||||
this.q.set(oldq);
|
||||
}
|
||||
this.q.set(collection, this.woffset);
|
||||
this.woffset += collection.byteLength;
|
||||
},
|
||||
|
||||
getLength: function() {
|
||||
return this.woffset-this.roffset;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
wdi.Queue = $.spcExtend(wdi.DomainObject, {
|
||||
q: null,
|
||||
raw: false,
|
||||
|
||||
init: function(c) {
|
||||
if(c.raw) {
|
||||
this.raw = c.raw;
|
||||
}
|
||||
this.q = new Uint8Array(0);
|
||||
},
|
||||
|
||||
getData: function() {
|
||||
return this.toJSArray(this.q);
|
||||
},
|
||||
|
||||
setData: function(q) {
|
||||
this.q = new Uint8Array(q.length);
|
||||
this.q.set(q);
|
||||
},
|
||||
|
||||
shift: function() {
|
||||
var elements = arguments[0] || this.getLength();
|
||||
|
||||
if (elements === this.q.length) {
|
||||
var toreturn = this.q;
|
||||
this.q = new Uint8Array(0);
|
||||
} else {
|
||||
var toreturn = this.q.subarray(0, elements);
|
||||
this.q = this.q.subarray(elements);
|
||||
}
|
||||
|
||||
return this.toJSArray(toreturn)
|
||||
},
|
||||
|
||||
peek: function(begin, end) {
|
||||
var tmp = null;
|
||||
if(begin == 0 && !end) {
|
||||
tmp = this.q; //read the entire queue
|
||||
} else {
|
||||
tmp = this.q.subarray(begin, end);
|
||||
}
|
||||
return this.toJSArray(tmp);
|
||||
},
|
||||
|
||||
push: function(collection) {
|
||||
if (typeof collection == 'string') {
|
||||
var len = collection.length;
|
||||
var newq = new Uint8Array(this.q.length+len);
|
||||
newq.set(this.q);
|
||||
for(var i=0;i<len;i++) {
|
||||
newq[i+this.q.length] = collection[i];
|
||||
}
|
||||
this.q = newq;
|
||||
} else {
|
||||
if(this.getLength() === 0) {
|
||||
this.q = new Uint8Array(collection.length);
|
||||
this.q.set(collection);
|
||||
} else {
|
||||
var newq = new Uint8Array(collection.length+this.q.length);
|
||||
newq.set(this.q);
|
||||
newq.set(collection, this.q.length);
|
||||
this.q = newq;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getLength: function() {
|
||||
return this.q.length;
|
||||
},
|
||||
|
||||
toJSArray: function(data) {
|
||||
if(this.raw) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return data.toJSArray();
|
||||
}
|
||||
});
|
||||
|
||||
wdi.ViewQueue = $.spcExtend(wdi.DomainObject, {
|
||||
q: null,
|
||||
at: null,
|
||||
|
||||
init: function() {
|
||||
this.q = new Uint8Array();
|
||||
this.at = 0;
|
||||
},
|
||||
|
||||
getData: function() {
|
||||
return this.toJSArray(this.q.subarray(this.at));
|
||||
},
|
||||
|
||||
getDataOffset: function(pos) {
|
||||
return this.toJSArray(this.q.subarray(pos));
|
||||
},
|
||||
|
||||
getRawData: function() {
|
||||
return this.q.subarray(this.at);
|
||||
},
|
||||
|
||||
getRawDataOffset: function(pos) {
|
||||
return this.q.subarray(pos);
|
||||
},
|
||||
|
||||
setData: function(q) {
|
||||
this.q = new Uint8Array(q.length);
|
||||
this.q.set(q);
|
||||
this.at = 0;
|
||||
},
|
||||
|
||||
shift: function(length) {
|
||||
var elements = length || this.getLength();
|
||||
if(elements > this.getLength()) {
|
||||
elements = this.getLength();
|
||||
}
|
||||
var ret = this.q.subarray(0+this.at, elements+this.at);
|
||||
this.at += elements;
|
||||
return this.toJSArray(ret);
|
||||
},
|
||||
|
||||
eatBytes: function(bytes) {
|
||||
this.at += bytes;
|
||||
},
|
||||
|
||||
getByte: function(pos) {
|
||||
return this.q[pos+this.at];
|
||||
},
|
||||
|
||||
peek: function(begin, end) {
|
||||
var tmp = null;
|
||||
if(begin == 0 && !end) {
|
||||
tmp = this.q; //read the entire queue
|
||||
} else {
|
||||
if(end) {
|
||||
end += this.at;
|
||||
}
|
||||
tmp = this.q.subarray(begin+this.at, end);
|
||||
}
|
||||
return this.toJSArray(tmp);
|
||||
},
|
||||
|
||||
push: function(collection) {
|
||||
if (typeof collection == 'string') {
|
||||
var len = collection.length;
|
||||
var newq = new Uint8Array(this.q.length+len);
|
||||
newq.set(this.q);
|
||||
for(var i=0;i<len;i++) {
|
||||
newq[i+this.q.length] = collection[i];
|
||||
}
|
||||
this.q = newq;
|
||||
} else {
|
||||
if(this.getLength() === 0) {
|
||||
this.q = new Uint8Array(collection.length);
|
||||
this.q.set(collection);
|
||||
} else {
|
||||
var newq = new Uint8Array(collection.length+this.q.length);
|
||||
newq.set(this.q);
|
||||
newq.set(collection, this.q.length);
|
||||
this.q = newq;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getLength: function() {
|
||||
return this.q.length-this.at;
|
||||
},
|
||||
|
||||
getPosition: function() {
|
||||
return this.at;
|
||||
},
|
||||
toJSArray: function(data) {
|
||||
if(data.length == 1) {
|
||||
return [data[0]];
|
||||
}
|
||||
return data.toJSArray();
|
||||
}
|
||||
});
|
||||
|
542
js/spice-web-client/lib/rasterEngine.js
Normal file
542
js/spice-web-client/lib/rasterEngine.js
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.RasterEngine = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function(c) {
|
||||
this.clientGui = c.clientGui;
|
||||
},
|
||||
|
||||
drawCanvas: function(spiceMessage) {
|
||||
return this.clientGui.drawCanvas(spiceMessage);
|
||||
},
|
||||
|
||||
removeCanvas: function(spiceMessage) {
|
||||
return this.clientGui.removeCanvas(spiceMessage);
|
||||
},
|
||||
|
||||
invalList: function(spiceMessage) {
|
||||
var items = spiceMessage.args.items;
|
||||
var item = null;
|
||||
for(var i in items) {
|
||||
item = items[i];
|
||||
wdi.ImageCache.delImage(item.id);
|
||||
}
|
||||
},
|
||||
|
||||
handleStreamCreate: function(spiceMessage) {
|
||||
var stream = spiceMessage.args;
|
||||
stream.computedBox = wdi.graphics.getBoxFromSrcArea(stream.rect);
|
||||
wdi.Stream.addStream(spiceMessage.args.id, stream);
|
||||
},
|
||||
|
||||
handleStreamData: function(spiceMessage) {
|
||||
var imageData = spiceMessage.args.data; //jpeg string encoded
|
||||
var stream = wdi.Stream.getStream(spiceMessage.args.id); //recover the stream
|
||||
var context = this.clientGui.getContext(stream.surface_id);
|
||||
var img = wdi.GlobalPool.create('Image'); //auto-release pool
|
||||
wdi.ExecutionControl.sync = true;
|
||||
var url;
|
||||
img.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
var box = stream.computedBox;
|
||||
// we only rotate the stream if spice tells us so through the TOP_DOWN flag mask
|
||||
if (!stream.flags & wdi.SpiceStreamFlags.SPICE_STREAM_FLAGS_TOP_DOWN) {
|
||||
var offsetX = box.x + (this.width/2);
|
||||
var offsetY = box.y + (this.height/2);
|
||||
context.save();
|
||||
context.translate(offsetX, offsetY);
|
||||
context.rotate(Math.PI);
|
||||
context.scale(-1,1);
|
||||
context.drawImage(this, box.x-offsetX, box.y-offsetY, box.width, box.height);
|
||||
context.restore();
|
||||
} else {
|
||||
context.drawImage(this, box.x, box.y, box.width, box.height);
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
URL.revokeObjectURL(url)
|
||||
};
|
||||
|
||||
url = wdi.SpiceObject.bytesToURI(imageData);
|
||||
img.src = url;
|
||||
},
|
||||
|
||||
handleStreamClip: function(spiceMessage) {
|
||||
wdi.Stream.clip(spiceMessage.args.id, spiceMessage.args.clip)
|
||||
},
|
||||
|
||||
handleStreamDestroy: function(spiceMessage) {
|
||||
wdi.Stream.deleteStream(spiceMessage.args.id);
|
||||
},
|
||||
|
||||
drawRop3: function(spiceMessage) {
|
||||
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.base.box);
|
||||
var context = this.clientGui.getContext(spiceMessage.args.base.surface_id);
|
||||
var destImg = context.getImageData(box.x, box.y, box.width, box.height);
|
||||
var clientGui = this.clientGui;
|
||||
|
||||
var brush = spiceMessage.args.brush;
|
||||
var rop = spiceMessage.args.rop_descriptor;
|
||||
|
||||
var srcArea = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
|
||||
wdi.graphics.getImageFromSpice(spiceMessage.args.src_image.imageDescriptor, spiceMessage.args.src_image.data, this.clientGui, function (sourceCanvas) {
|
||||
if (sourceCanvas) {
|
||||
//Get source image data (image coming from the packet)
|
||||
var sourceContext = sourceCanvas.getContext('2d');
|
||||
var srcImg = sourceContext.getImageData(srcArea.x, srcArea.y, srcArea.width, srcArea.height);
|
||||
var srcImgData = srcImg.data; //this
|
||||
|
||||
//Get pattern image data
|
||||
//brush
|
||||
var tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height);
|
||||
var tmpcontext = tmpcanvas.getContext('2d');
|
||||
var brushBox = {
|
||||
width: box.width,
|
||||
height: box.height,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
wdi.graphics.setBrush(clientGui, tmpcontext, brush, brushBox, wdi.SpiceRopd.SPICE_ROPD_OP_PUT);//Without alpha?
|
||||
var pattern = tmpcontext.getImageData(0, 0, box.width, box.height);
|
||||
var patImgData = pattern.data; //this
|
||||
|
||||
//Get dest image data
|
||||
var destImgData = destImg.data;
|
||||
|
||||
//Get result image data
|
||||
tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height);
|
||||
tmpcontext = tmpcanvas.getContext('2d');
|
||||
var result = tmpcontext.createImageData(box.width, box.height);
|
||||
var resultData = result.data;
|
||||
|
||||
if ((srcImg.width != pattern.width || srcImg.width != destImg.width) || (srcImg.height != pattern.height || srcImg.height != destImg.height)) {
|
||||
//TODO: resize
|
||||
}
|
||||
|
||||
//Do the Ternary Raster Operation
|
||||
var length = destImgData.length;//Could be anyone
|
||||
var func = wdi.Rop3[rop];
|
||||
for (var i = 0;i<length;i+=4) {
|
||||
resultData[i] = func(patImgData[i], srcImgData[i], destImgData[i]) & 255;
|
||||
resultData[i+1] = func(patImgData[i+1], srcImgData[i+1], destImgData[i+1]) & 255;
|
||||
resultData[i+2] = func(patImgData[i+2], srcImgData[i+2], destImgData[i+2]) & 255;
|
||||
resultData[i+3] = 255;
|
||||
}
|
||||
|
||||
tmpcontext.putImageData(result, 0, 0);
|
||||
|
||||
this.drawClip(tmpcanvas, box, context);
|
||||
} else {
|
||||
wdi.Debug.log('Unable to get image!');
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
drawInvers: function(spiceMessage) {
|
||||
var drawBase = spiceMessage.args.base;
|
||||
var box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
|
||||
|
||||
var surface_id = drawBase.surface_id;
|
||||
|
||||
var context = this.clientGui.getContext(surface_id);
|
||||
|
||||
var destImg = wdi.graphics.getRect(box, context.canvas);
|
||||
var imageData = wdi.RasterOperation.process(wdi.SpiceRopd.SPICE_ROPD_OP_INVERS, null, destImg);//this operation modifies destination
|
||||
|
||||
context.drawImage(imageData, box.x, box.y, box.width, box.height);
|
||||
},
|
||||
|
||||
drawStroke: function(spiceMessage) {
|
||||
var stroke = spiceMessage.args,
|
||||
context = this.clientGui.getContext(spiceMessage.args.base.surface_id),
|
||||
color = stroke.brush.color.html_color,
|
||||
lineWidth = 1,
|
||||
pointsLength,
|
||||
firstPoint,
|
||||
i,
|
||||
j,
|
||||
length = stroke.path.num_segments,
|
||||
seg;
|
||||
|
||||
if (stroke.attr.flags & wdi.SpiceLineFlags.SPICE_LINE_FLAGS_STYLED) {
|
||||
wdi.Debug.log('SPICE_LINE_FLAGS_STYLED');
|
||||
}
|
||||
|
||||
for (var i = 0;i < length; i++) {
|
||||
seg = stroke.path.segments[i];
|
||||
|
||||
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_BEGIN) {
|
||||
context.beginPath();
|
||||
context.moveTo(seg.points[0].x, seg.points[0].y);
|
||||
context.strokeStyle = color;
|
||||
context.lineWidth = lineWidth;
|
||||
}
|
||||
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_BEZIER) {
|
||||
pointsLength = seg.points.length;
|
||||
if (pointsLength % 3 == 0) {
|
||||
for (j = 0; j < pointsLength; j += 3) {
|
||||
context.bezierCurveTo(
|
||||
seg.points[j].x, seg.points[j].y,
|
||||
seg.points[j+1].x, seg.points[j+1].y,
|
||||
seg.points[j+2].x, seg.points[j+2].y
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pointsLength = seg.points.length;
|
||||
|
||||
for (j = 0; j < pointsLength; j++) {
|
||||
if (j == 0) firstPoint = seg.points[j];
|
||||
context.lineTo(seg.points[j].x + (lineWidth / 2), seg.points[j].y + (lineWidth / 2));
|
||||
}
|
||||
}
|
||||
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_END) {
|
||||
if (seg.flags & wdi.SpicePathFlags.SPICE_PATH_CLOSE) {
|
||||
context.lineTo(firstPoint.x + (lineWidth / 2), firstPoint.y + (lineWidth / 2));
|
||||
}
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
drawImage: function(spiceMessage) {
|
||||
var args = spiceMessage.args;
|
||||
var drawBase = args.base;
|
||||
var surface_id = drawBase.surface_id;
|
||||
var rop = args.rop_descriptor;
|
||||
|
||||
var scale = args.scale_mode;
|
||||
|
||||
|
||||
//calculate src_area box
|
||||
var box_origin = wdi.graphics.getBoxFromSrcArea(args.src_area);
|
||||
|
||||
var box_dest = wdi.graphics.getBoxFromSrcArea(drawBase.box);
|
||||
|
||||
//depending on the rop, we can avoid to get destImg
|
||||
if (rop === wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
|
||||
var destImg = null;
|
||||
} else {
|
||||
//get the destination image, there is a ROP
|
||||
var destImg = wdi.graphics.getRect(box_dest, this.clientGui.getCanvas(surface_id));
|
||||
}
|
||||
|
||||
if (window.vdiLoadTest && window.firstImage === undefined) {
|
||||
window.firstImage = true;
|
||||
}
|
||||
|
||||
//get the image in imagedata format
|
||||
wdi.graphics.getImageFromSpice(args.image.imageDescriptor, args.image.data, this.clientGui, function(srcImg) {
|
||||
//we have image?
|
||||
if(srcImg) {
|
||||
if (window.firstImage) {
|
||||
var data;
|
||||
if(srcImg.getContext) {
|
||||
data = srcImg.getContext('2d').getImageData(0, 0, srcImg.width, srcImg.height).data.buffer.slice(0)
|
||||
} else {
|
||||
data = srcImg.data.buffer.slice(0);
|
||||
}
|
||||
window.firstImageData = data;
|
||||
window.firstImage = false;
|
||||
}
|
||||
|
||||
//adapt to src_area
|
||||
srcImg = wdi.graphics.getRect(box_origin, srcImg);
|
||||
if(box_origin.width !== box_dest.width && box_origin.height !== box_dest.height) {
|
||||
srcImg = wdi.graphics.getImageFromData(srcImg);
|
||||
var newSrcImg = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
|
||||
var tmpcontext = newSrcImg.getContext('2d');
|
||||
tmpcontext.drawImage(srcImg, 0, 0, box_origin.width, box_origin.height, 0, 0, box_dest.width, box_dest.height);
|
||||
srcImg = newSrcImg;
|
||||
}
|
||||
|
||||
//rop
|
||||
srcImg = wdi.RasterOperation.process(rop, srcImg, destImg);
|
||||
|
||||
var context = this.clientGui.getContext(surface_id);
|
||||
|
||||
//TODO: swcanvas do not support clipping
|
||||
if(args.base.clip.type === wdi.SpiceClipType.SPICE_CLIP_TYPE_RECTS) {
|
||||
srcImg = wdi.graphics.getImageFromData(srcImg);
|
||||
}
|
||||
|
||||
if(srcImg instanceof ImageData) {
|
||||
context.putImageData(srcImg, box_dest.x, box_dest.y, 0, 0, box_dest.width, box_dest.height);
|
||||
} else {
|
||||
context.drawImage(srcImg, box_dest.x, box_dest.y, box_dest.width, box_dest.height);
|
||||
}
|
||||
|
||||
} else {
|
||||
//failed to get image, cache error?
|
||||
wdi.Debug.log('Unable to get image!');
|
||||
}
|
||||
}, this, {'opaque':true, 'brush': args.brush, 'raw': false});
|
||||
},
|
||||
|
||||
drawClip: function(srcImg, box, context) {
|
||||
context.drawImage(srcImg, box.x, box.y, box.width, box.height);
|
||||
},
|
||||
|
||||
drawFill: function(spiceMessage) {
|
||||
var args = spiceMessage.args;
|
||||
var context = this.clientGui.getContext(args.base.surface_id);
|
||||
var box = wdi.graphics.getBoxFromSrcArea(args.base.box);
|
||||
var brush = args.brush;
|
||||
var ropd = args.rop_descriptor;
|
||||
|
||||
wdi.graphics.setBrush(this.clientGui, context, brush, box, ropd);
|
||||
},
|
||||
|
||||
drawCopyBits: function(spiceMessage) {
|
||||
var drawBase = spiceMessage.args.base;
|
||||
var surface_id = drawBase.surface_id;
|
||||
var src_position = spiceMessage.args.src_position;
|
||||
var context = this.clientGui.getContext(surface_id);
|
||||
var box = drawBase.box;
|
||||
|
||||
var width = box.right - box.left;
|
||||
var height = box.bottom - box.top;
|
||||
|
||||
context.drawImage(context.canvas, src_position.x, src_position.y, width,
|
||||
height, drawBase.box.left, drawBase.box.top, width, height);
|
||||
},
|
||||
|
||||
drawBlend: function(spiceMessage) {
|
||||
//TODO: alpha_flags
|
||||
//TODO: resize
|
||||
var descriptor = spiceMessage.args.image.imageDescriptor;
|
||||
var drawBase = spiceMessage.args.base;
|
||||
var imgData = spiceMessage.args.image.data;
|
||||
var surface_id = spiceMessage.args.base.surface_id;
|
||||
var rop_desc = spiceMessage.args.rop_descriptor;
|
||||
var flags = spiceMessage.args.flags;
|
||||
|
||||
wdi.graphics.getImageFromSpice(descriptor, imgData, this.clientGui, function(srcImg) {
|
||||
if (!srcImg) {
|
||||
wdi.Debug.log('There is no image on Blend');
|
||||
return;
|
||||
}
|
||||
|
||||
//get box from src area
|
||||
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
|
||||
|
||||
//adapt to src_area
|
||||
srcImg = wdi.graphics.getRect(box, srcImg);
|
||||
|
||||
//destination box
|
||||
var dest_box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
|
||||
var destImg = wdi.graphics.getRect(dest_box, this.clientGui.getCanvas(surface_id));
|
||||
|
||||
var result = wdi.RasterOperation.process(rop_desc, srcImg, destImg);
|
||||
|
||||
this.clientGui.getCanvas(surface_id).getContext('2d').drawImage(result, dest_box.x, dest_box.y, dest_box.width, dest_box.height);
|
||||
}, this);
|
||||
},
|
||||
|
||||
drawAlphaBlend: function(spiceMessage) {
|
||||
|
||||
//TODO: alpha_flags
|
||||
//TODO: resize
|
||||
|
||||
var descriptor = spiceMessage.args.image.imageDescriptor;
|
||||
var drawBase = spiceMessage.args.base;
|
||||
var imgData = spiceMessage.args.image.data;
|
||||
var surface_id = spiceMessage.args.base.surface_id;
|
||||
var flags = spiceMessage.args.alpha_flags;
|
||||
var alpha = spiceMessage.args.alpha;
|
||||
|
||||
wdi.graphics.getImageFromSpice(descriptor, imgData, this.clientGui, function(srcImg) {
|
||||
if (!srcImg) {
|
||||
wdi.Debug.log('There is no image on drawAlphaBlend');
|
||||
}
|
||||
|
||||
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
|
||||
|
||||
//adapt to src_area
|
||||
srcImg = wdi.graphics.getRect(box, srcImg);
|
||||
|
||||
|
||||
//destination box
|
||||
var box_dest = wdi.graphics.getBoxFromSrcArea(drawBase.box);
|
||||
var destImg = wdi.graphics.getRect(box_dest, this.clientGui.getCanvas(surface_id));
|
||||
|
||||
if(box.width !== box_dest.width && box.height !== box_dest.height) {
|
||||
var tmpcanvas = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
|
||||
var tmpcontext = tmpcanvas.getContext('2d');
|
||||
tmpcontext.drawImage(srcImg, 0, 0, box.width, box.height, 0, 0, box_dest.width, box_dest.height);
|
||||
srcImg = tmpcanvas;
|
||||
}
|
||||
|
||||
var src = wdi.graphics.getDataFromImage(srcImg).data;
|
||||
var dst = wdi.graphics.getDataFromImage(destImg).data;
|
||||
|
||||
var length = src.length-1;
|
||||
|
||||
//create a new imagedata to store result
|
||||
var imageResult = wdi.graphics.getNewTmpCanvas(box_dest.width, box_dest.height);
|
||||
var context = imageResult.getContext('2d');
|
||||
|
||||
var resultImageData = context.createImageData(box_dest.width, box_dest.height);
|
||||
var result = resultImageData.data;
|
||||
|
||||
var rS, rD;
|
||||
var gS, gD;
|
||||
var bS, bD;
|
||||
var aS;
|
||||
|
||||
for (var px=0;px<length;px+=4) {
|
||||
rS = src[px];
|
||||
gS = src[px+1];
|
||||
bS = src[px+2];
|
||||
|
||||
if(flags || alpha === 255) {
|
||||
aS = src[px+3];
|
||||
} else {
|
||||
aS = alpha;
|
||||
}
|
||||
|
||||
rD = dst[px];
|
||||
gD = dst[px+1];
|
||||
bD = dst[px+2];
|
||||
|
||||
if(aS > 30 && alpha === 255) {
|
||||
//formula from reactos, this is premultiplied alpha values
|
||||
result[px] = ((rD * (255 - aS)) / 255 + rS) & 0xff;
|
||||
result[px+1] = ((gD * (255 - aS)) / 255 + gS) & 0xff;
|
||||
result[px+2] = ((bD * (255 - aS)) / 255 + bS) & 0xff;
|
||||
} else {
|
||||
//homemade blend function, this is the typical blend function simplified
|
||||
result[px] = (( (rS*aS)+(rD*(255-aS)) ) / 255) & 0xff;
|
||||
result[px+1] = (( (gS*aS)+(gD*(255-aS)) ) / 255) & 0xff;
|
||||
result[px+2] = (( (bS*aS)+(bD*(255-aS)) ) / 255) & 0xff;
|
||||
}
|
||||
|
||||
result[px+3] = 255;
|
||||
}
|
||||
imageResult.getContext('2d').putImageData(resultImageData, 0, 0);
|
||||
|
||||
this.drawClip(imageResult, box_dest, this.clientGui.getContext(surface_id));
|
||||
}, this);
|
||||
},
|
||||
|
||||
drawWhiteness: function(spiceMessage) {
|
||||
//TODO: mask
|
||||
var base = spiceMessage.args.base;
|
||||
var context = this.clientGui.getContext(base.surface_id);
|
||||
var box = wdi.graphics.getBoxFromSrcArea(base.box);
|
||||
context.fillStyle = "white";
|
||||
context.fillRect(box.x, box.y, box.width, box.height);
|
||||
},
|
||||
|
||||
drawBlackness: function(spiceMessage) {
|
||||
//TODO: mask
|
||||
var base = spiceMessage.args.base;
|
||||
var context = this.clientGui.getContext(base.surface_id);
|
||||
var box = wdi.graphics.getBoxFromSrcArea(base.box);
|
||||
context.fillStyle = "black";
|
||||
context.fillRect(box.x, box.y, box.width, box.height);
|
||||
},
|
||||
|
||||
drawTransparent: function(spiceMessage) {
|
||||
var drawBase = spiceMessage.args.base;
|
||||
var surface_id = drawBase.surface_id;
|
||||
|
||||
//calculate src_area box
|
||||
var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area);
|
||||
var dest_box = wdi.graphics.getBoxFromSrcArea(drawBase.box);
|
||||
|
||||
//get destination iamge, in imagedata format because is what we need
|
||||
var destImg = this.clientGui.getContext(surface_id).getImageData(dest_box.x, dest_box.y,
|
||||
dest_box.width, dest_box.height);
|
||||
|
||||
wdi.graphics.getImageFromSpice(spiceMessage.args.image.imageDescriptor, spiceMessage.args.image.data, this.clientGui, function(srcImg) {
|
||||
if(srcImg) {
|
||||
//adapt to src_area
|
||||
srcImg = wdi.graphics.getRect(box, srcImg);
|
||||
|
||||
var source = wdi.graphics.getDataFromImage(srcImg).data;
|
||||
var dest = destImg.data;
|
||||
|
||||
var length = source.length-1;
|
||||
var resultImageData = this.clientGui.getContext(surface_id).createImageData(dest_box.width, dest_box.height);
|
||||
|
||||
var color = spiceMessage.args.transparent_true_color;
|
||||
while(length>0) {
|
||||
resultImageData.data[length] = 255; //alpha
|
||||
if(source[length-1] === color.b && source[length-2] === color.g
|
||||
&& source[length-3] === color.r) {
|
||||
resultImageData.data[length-1] = dest[length-1]; //b
|
||||
resultImageData.data[length-2] = dest[length-2]; //g
|
||||
resultImageData.data[length-3] = dest[length-3]; //r
|
||||
} else {
|
||||
resultImageData.data[length-1] = source[length-1]; //b
|
||||
resultImageData.data[length-2] = source[length-2]; //g
|
||||
resultImageData.data[length-3] = source[length-3]; //r
|
||||
}
|
||||
|
||||
length-=4;
|
||||
}
|
||||
var resultImage = wdi.graphics.getImageFromData(resultImageData);
|
||||
this.drawClip(resultImage, dest_box, this.clientGui.getContext(surface_id));
|
||||
} else {
|
||||
//failed to get image, cache error?
|
||||
wdi.Debug.log('Unable to get image!');
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
drawText: function(spiceMessage) {
|
||||
var context = this.clientGui.getContext(spiceMessage.args.base.surface_id);
|
||||
var bbox = spiceMessage.args.base.box;
|
||||
var clip = spiceMessage.args.base.clip;
|
||||
var text = spiceMessage.args;
|
||||
var string = text.glyph_string;
|
||||
var bpp = string.flags === 1 ? 1 : string.flags * 2;
|
||||
|
||||
if (text.back_mode !== 0) {
|
||||
wdi.graphics.drawBackText(this.clientGui, context, text);
|
||||
}
|
||||
|
||||
wdi.graphics.drawString(context, string, bpp, text.fore_brush, clip.type, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all color palettes
|
||||
* @param spiceMessage
|
||||
* @param app
|
||||
*/
|
||||
invalPalettes: function(spiceMessage) {
|
||||
wdi.ImageCache.clearPalettes();
|
||||
}
|
||||
});
|
68
js/spice-web-client/lib/rng.js
Normal file
68
js/spice-web-client/lib/rng.js
Normal file
@ -0,0 +1,68 @@
|
||||
// Random number generator - requires a PRNG backend, e.g. prng4.js
|
||||
|
||||
// For best results, put code like
|
||||
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
|
||||
// in your main HTML document.
|
||||
|
||||
var rng_state;
|
||||
var rng_pool;
|
||||
var rng_pptr;
|
||||
|
||||
// Mix in a 32-bit integer into the pool
|
||||
function rng_seed_int(x) {
|
||||
rng_pool[rng_pptr++] ^= x & 255;
|
||||
rng_pool[rng_pptr++] ^= (x >> 8) & 255;
|
||||
rng_pool[rng_pptr++] ^= (x >> 16) & 255;
|
||||
rng_pool[rng_pptr++] ^= (x >> 24) & 255;
|
||||
if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
|
||||
}
|
||||
|
||||
// Mix in the current time (w/milliseconds) into the pool
|
||||
function rng_seed_time() {
|
||||
rng_seed_int(new Date().getTime());
|
||||
}
|
||||
|
||||
// Initialize the pool with junk if needed.
|
||||
if(rng_pool == null) {
|
||||
rng_pool = new Array();
|
||||
rng_pptr = 0;
|
||||
var t;
|
||||
if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
|
||||
// Extract entropy (256 bits) from NS4 RNG if available
|
||||
var z = window.crypto.random(32);
|
||||
for(t = 0; t < z.length; ++t)
|
||||
rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
|
||||
}
|
||||
while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
|
||||
t = Math.floor(65536 * Math.random());
|
||||
rng_pool[rng_pptr++] = t >>> 8;
|
||||
rng_pool[rng_pptr++] = t & 255;
|
||||
}
|
||||
rng_pptr = 0;
|
||||
rng_seed_time();
|
||||
//rng_seed_int(window.screenX);
|
||||
//rng_seed_int(window.screenY);
|
||||
}
|
||||
|
||||
function rng_get_byte() {
|
||||
if(rng_state == null) {
|
||||
rng_seed_time();
|
||||
rng_state = prng_newstate();
|
||||
rng_state.init(rng_pool);
|
||||
for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
|
||||
rng_pool[rng_pptr] = 0;
|
||||
rng_pptr = 0;
|
||||
//rng_pool = null;
|
||||
}
|
||||
// TODO: allow reseeding after first request
|
||||
return rng_state.next();
|
||||
}
|
||||
|
||||
function rng_get_bytes(ba) {
|
||||
var i;
|
||||
for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
|
||||
}
|
||||
|
||||
function SecureRandom() {}
|
||||
|
||||
SecureRandom.prototype.nextBytes = rng_get_bytes;
|
115
js/spice-web-client/lib/runqueue.js
Normal file
115
js/spice-web-client/lib/runqueue.js
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.RunQueue = $.spcExtend(wdi.DomainObject, {
|
||||
tasks: null,
|
||||
isRunning: false,
|
||||
|
||||
init: function() {
|
||||
this.tasks = [];
|
||||
},
|
||||
|
||||
getTasksLength: function() {
|
||||
return this.tasks.length;
|
||||
},
|
||||
|
||||
add: function(fn, scope, endCallback, params) {
|
||||
this.tasks.push({
|
||||
fn: fn,
|
||||
scope: scope,
|
||||
fnFinish: endCallback,
|
||||
params: params
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.tasks = [];
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_process: function() {
|
||||
wdi.ExecutionControl.sync = true;
|
||||
var proxy, self = this;
|
||||
this.isRunning = true;
|
||||
var task = this.tasks.shift();
|
||||
|
||||
if (!task) {
|
||||
this.isRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
proxy = {
|
||||
end: function() {
|
||||
if(task.fnFinish) {
|
||||
task.fnFinish.call(task.scope);
|
||||
}
|
||||
self._process();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
task.fn.call(task.scope, proxy, task.params);
|
||||
} catch(e) {
|
||||
wdi.Debug.error(e.message);
|
||||
proxy.end();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
process: function() {
|
||||
if (!this.isRunning) {
|
||||
this._process();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//wdi.ExecutionControl = $.spcExtend(wdi.DomainObject, {
|
||||
// currentProxy: null,
|
||||
// sync: true,
|
||||
// runQ: null,
|
||||
// init: function(c) {
|
||||
// this.runQ = c.runQ || new wdi.RunQueue();
|
||||
// }
|
||||
//});
|
||||
|
||||
//TODO: make an instance of it on each channel
|
||||
wdi.ExecutionControl = {
|
||||
currentProxy: null,
|
||||
sync: true,
|
||||
runQ: new wdi.RunQueue()
|
||||
};
|
105
js/spice-web-client/lib/sha1.js
Normal file
105
js/spice-web-client/lib/sha1.js
Normal file
@ -0,0 +1,105 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* SHA-1 implementation in JavaScript (c) Chris Veness 2002-2009 */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
function sha1Hash(msg)
|
||||
{
|
||||
// constants [§4.2.1]
|
||||
var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
|
||||
|
||||
|
||||
// PREPROCESSING
|
||||
|
||||
msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
|
||||
|
||||
// convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
|
||||
var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
|
||||
var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints
|
||||
var M = new Array(N);
|
||||
for (var i=0; i<N; i++) {
|
||||
M[i] = new Array(16);
|
||||
for (var j=0; j<16; j++) { // encode 4 chars per integer, big-endian encoding
|
||||
M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) |
|
||||
(msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
|
||||
}
|
||||
}
|
||||
// add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1]
|
||||
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts
|
||||
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
|
||||
M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14])
|
||||
M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;
|
||||
|
||||
// set initial hash value [§5.3.1]
|
||||
var H0 = 0x67452301;
|
||||
var H1 = 0xefcdab89;
|
||||
var H2 = 0x98badcfe;
|
||||
var H3 = 0x10325476;
|
||||
var H4 = 0xc3d2e1f0;
|
||||
|
||||
// HASH COMPUTATION [§6.1.2]
|
||||
|
||||
var W = new Array(80); var a, b, c, d, e;
|
||||
for (var i=0; i<N; i++) {
|
||||
|
||||
// 1 - prepare message schedule 'W'
|
||||
for (var t=0; t<16; t++) W[t] = M[i][t];
|
||||
for (var t=16; t<80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);
|
||||
|
||||
// 2 - initialise five working variables a, b, c, d, e with previous hash value
|
||||
a = H0; b = H1; c = H2; d = H3; e = H4;
|
||||
|
||||
// 3 - main loop
|
||||
for (var t=0; t<80; t++) {
|
||||
var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
|
||||
var T = (ROTL(a,5) + f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
|
||||
e = d;
|
||||
d = c;
|
||||
c = ROTL(b, 30);
|
||||
b = a;
|
||||
a = T;
|
||||
}
|
||||
|
||||
// 4 - compute the new intermediate hash value
|
||||
H0 = (H0+a) & 0xffffffff; // note 'addition modulo 2^32'
|
||||
H1 = (H1+b) & 0xffffffff;
|
||||
H2 = (H2+c) & 0xffffffff;
|
||||
H3 = (H3+d) & 0xffffffff;
|
||||
H4 = (H4+e) & 0xffffffff;
|
||||
}
|
||||
|
||||
return H0.toHexStr() + H1.toHexStr() + H2.toHexStr() + H3.toHexStr() + H4.toHexStr();
|
||||
}
|
||||
|
||||
//
|
||||
// function 'f' [§4.1.1]
|
||||
//
|
||||
function f(s, x, y, z)
|
||||
{
|
||||
switch (s) {
|
||||
case 0: return (x & y) ^ (~x & z); // Ch()
|
||||
case 1: return x ^ y ^ z; // Parity()
|
||||
case 2: return (x & y) ^ (x & z) ^ (y & z); // Maj()
|
||||
case 3: return x ^ y ^ z; // Parity()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// rotate left (circular left shift) value x by n positions [§3.2.5]
|
||||
//
|
||||
function ROTL(x, n)
|
||||
{
|
||||
return (x<<n) | (x>>>(32-n));
|
||||
}
|
||||
|
||||
//
|
||||
// extend Number class with a tailored hex-string method
|
||||
// (note toString(16) is implementation-dependant, and
|
||||
// in IE returns signed numbers when used on full words)
|
||||
//
|
||||
Number.prototype.toHexStr = function()
|
||||
{
|
||||
var s="", v;
|
||||
for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); }
|
||||
return s;
|
||||
}
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
122
js/spice-web-client/lib/stuckkeyshandler.js
Normal file
122
js/spice-web-client/lib/stuckkeyshandler.js
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.StuckKeysHandler = $.spcExtend(wdi.EventObject.prototype, {
|
||||
ctrlTimeoutId: null,
|
||||
altTimeoutId: null,
|
||||
shiftTimeoutId: null,
|
||||
shiftKeyPressed: false,
|
||||
ctrlKeyPressed: false,
|
||||
altKeyPressed: false,
|
||||
|
||||
handleStuckKeys: function (jqueryEvent) {
|
||||
if (jqueryEvent) {
|
||||
switch (jqueryEvent.keyCode) {
|
||||
case 16:
|
||||
this._handleKey('shiftTimeoutId', jqueryEvent.type, 16);
|
||||
break;
|
||||
case 17:
|
||||
this._handleKey('ctrlTimeoutId', jqueryEvent.type, 17);
|
||||
break;
|
||||
case 18:
|
||||
this._handleKey('altTimeoutId', jqueryEvent.type, 18);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
releaseAllKeys: function releaseAllKeys () {
|
||||
var e;
|
||||
var i;
|
||||
for (i = 0; i < 300; i++) {
|
||||
this.releaseKeyPressed(i);
|
||||
}
|
||||
},
|
||||
|
||||
_handleKey: function (variable, type, keyCode) {
|
||||
if (type === 'keydown') {
|
||||
this[variable] = this._configureTimeout(keyCode);
|
||||
} else if (type === 'keyup') {
|
||||
clearTimeout(this[variable]);
|
||||
}
|
||||
},
|
||||
|
||||
_configureTimeout: function (keyCode) {
|
||||
var self = this;
|
||||
return setTimeout(function keyPressedTimeout () {
|
||||
// added the 'window' for the jQuery call for testing.
|
||||
self.releaseKeyPressed(keyCode);
|
||||
}, wdi.StuckKeysHandler.defaultTimeout);
|
||||
},
|
||||
|
||||
releaseKeyPressed: function (keyCode) {
|
||||
var e = window.jQuery.Event("keyup");
|
||||
e["which"] = keyCode;
|
||||
e["keyCode"] = keyCode;
|
||||
e["charCode"] = 0;
|
||||
e["generated"] = true;
|
||||
this.fire('inputStuck', ['keyup', [e]]);
|
||||
},
|
||||
|
||||
checkSpecialKey: function (event, keyCode) {
|
||||
switch (keyCode) {
|
||||
case 16:
|
||||
this.shiftKeyPressed = event === 'keydown';
|
||||
break;
|
||||
case 17:
|
||||
this.ctrlKeyPressed = event === 'keydown';
|
||||
break;
|
||||
case 18:
|
||||
this.altKeyPressed = event === 'keydown';
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
releaseSpecialKeysPressed: function () {
|
||||
if (this.shiftKeyPressed) {
|
||||
this.releaseKeyPressed(16);
|
||||
this.shiftKeyPressed = false;
|
||||
}
|
||||
if (this.ctrlKeyPressed) {
|
||||
this.releaseKeyPressed(17);
|
||||
this.ctrlKeyPressed = false;
|
||||
}
|
||||
if (this.altKeyPressed) {
|
||||
this.releaseKeyPressed(18);
|
||||
this.altKeyPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
wdi.StuckKeysHandler.defaultTimeout = 2000;
|
72
js/spice-web-client/lib/timelapsedetector.js
Normal file
72
js/spice-web-client/lib/timelapsedetector.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
|
||||
wdi.TimeLapseDetector = $.spcExtend(wdi.EventObject.prototype, {
|
||||
lastTime: null,
|
||||
|
||||
init: function timeLapseDetector_Init (c) {
|
||||
this.superInit();
|
||||
},
|
||||
|
||||
startTimer: function timeLapseDetector_startTimer () {
|
||||
var self = this;
|
||||
this.lastTime = Date.now();
|
||||
|
||||
window.setInterval(
|
||||
function timeLapseDetectorInterval () {
|
||||
var now = Date.now();
|
||||
// this.constructor == access to the class itself, so you
|
||||
// can access to static properties without writing/knowing
|
||||
// the class name
|
||||
var elapsed = now - self.lastTime;
|
||||
if (elapsed >= self.constructor.maxIntervalAllowed) {
|
||||
self.fire('timeLapseDetected', elapsed);
|
||||
}
|
||||
self.lastTime = now;
|
||||
},
|
||||
wdi.TimeLapseDetector.defaultInterval
|
||||
);
|
||||
},
|
||||
|
||||
getLastTime: function timeLapseDetector_getLastTime () {
|
||||
return this.lastTime;
|
||||
},
|
||||
|
||||
setLastTime: function timeLapseDetector_setLastTime (lastTime) {
|
||||
this.lastTime = lastTime;
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
wdi.TimeLapseDetector.defaultInterval = 5000;
|
||||
wdi.TimeLapseDetector.maxIntervalAllowed = wdi.TimeLapseDetector.defaultInterval * 3;
|
337
js/spice-web-client/lib/utils.js
Normal file
337
js/spice-web-client/lib/utils.js
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
//If we are in NODE
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
|
||||
jQuery = $ = {
|
||||
isArray: function (obj) {
|
||||
return Object.prototype.toString.apply(obj) === "[object Array]"
|
||||
},
|
||||
isPlainObject: function( obj ) {
|
||||
var key;
|
||||
|
||||
// Must be an Object.
|
||||
// Because of IE, we also have to check the presence of the constructor property.
|
||||
// Make sure that DOM nodes and window objects don't pass through, as well
|
||||
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not own constructor property must be Object
|
||||
if ( obj.constructor &&
|
||||
!core_hasOwn.call(obj, "constructor") &&
|
||||
!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
for ( key in obj ) {}
|
||||
|
||||
return key === undefined || core_hasOwn.call( obj, key );
|
||||
},
|
||||
extend: function() {
|
||||
var options, name, src, copy, copyIsArray, clone,
|
||||
target = arguments[0] || {},
|
||||
i = 1,
|
||||
length = arguments.length,
|
||||
deep = false;
|
||||
|
||||
// Handle a deep copy situation
|
||||
if ( typeof target === "boolean" ) {
|
||||
deep = target;
|
||||
target = arguments[1] || {};
|
||||
// skip the boolean and the target
|
||||
i = 2;
|
||||
}
|
||||
|
||||
// extend jQuery itself if only one argument is passed
|
||||
if ( length === i ) {
|
||||
target = this;
|
||||
--i;
|
||||
}
|
||||
|
||||
for ( ; i < length; i++ ) {
|
||||
// Only deal with non-null/undefined values
|
||||
if ( (options = arguments[ i ]) != null ) {
|
||||
// Extend the base object
|
||||
for ( name in options ) {
|
||||
src = target[ name ];
|
||||
copy = options[ name ];
|
||||
|
||||
// Prevent never-ending loop
|
||||
if ( target === copy ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurse if we're merging plain objects or arrays
|
||||
if ( deep && copy && ( this.isPlainObject(copy) || (copyIsArray = this.isArray(copy)) ) ) {
|
||||
if ( copyIsArray ) {
|
||||
copyIsArray = false;
|
||||
clone = src && this.isArray(src) ? src : [];
|
||||
|
||||
} else {
|
||||
clone = src && this.isPlainObject(src) ? src : {};
|
||||
}
|
||||
|
||||
// Never move original objects, clone them
|
||||
target[ name ] = this.extend( deep, clone, copy );
|
||||
|
||||
// Don't bring in undefined values
|
||||
} else if ( copy !== undefined ) {
|
||||
target[ name ] = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified object
|
||||
return target;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$.extend({
|
||||
spcExtend: function(obj) {
|
||||
var f = function(c) {
|
||||
if(typeof this['init'] !== 'undefined') {
|
||||
this.init(c || {});
|
||||
}
|
||||
};
|
||||
f.prototype.superInit = obj.init;
|
||||
var args = [];
|
||||
args.push(f.prototype);
|
||||
var length = arguments.length;
|
||||
for(var i =0; i<length;i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
|
||||
$.extend.apply($, args);
|
||||
return f;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(String.prototype, {
|
||||
lpad: function(padString, length) {
|
||||
var str = this;
|
||||
while (str.length < length)
|
||||
str = padString + str;
|
||||
return str;
|
||||
},
|
||||
|
||||
rpad: function(padString, length) {
|
||||
var str = this;
|
||||
while (str.length < length)
|
||||
str = str + padString;
|
||||
return str;
|
||||
}
|
||||
});
|
||||
|
||||
wdi = {};
|
||||
|
||||
wdi.DomainObject = {};
|
||||
|
||||
wdi.RawMessage = $.spcExtend(wdi.DomainObject, {
|
||||
status: null,
|
||||
data: null,
|
||||
|
||||
init: function(c) {
|
||||
this.status = c.status;
|
||||
this.data = c.data;
|
||||
}
|
||||
});
|
||||
|
||||
wdi.RawSpiceMessage = $.spcExtend(wdi.DomainObject, {
|
||||
header: null,
|
||||
body: null,
|
||||
channel: null,
|
||||
|
||||
set: function(header, body, channel) {
|
||||
this.header = header;
|
||||
this.body = body;
|
||||
this.channel = channel;
|
||||
}
|
||||
});
|
||||
|
||||
wdi.SpiceMessage = $.spcExtend(wdi.DomainObject, {
|
||||
messageType: null,
|
||||
args: null,
|
||||
channel: null,
|
||||
|
||||
init: function(c) {
|
||||
this.channel = c.channel;
|
||||
this.messageType = c.messageType;
|
||||
this.args = c.args;
|
||||
}
|
||||
});
|
||||
|
||||
wdi.EventObject = $.spcExtend(wdi.DomainObject, {
|
||||
events: null,
|
||||
|
||||
init: function() {
|
||||
this.eyeEvents = {};
|
||||
},
|
||||
|
||||
getListenersLength: function(eventName) {
|
||||
if (this.eyeEvents[eventName] == undefined) {
|
||||
this.eyeEvents[eventName] = [];
|
||||
}
|
||||
|
||||
return this.eyeEvents[eventName].length;
|
||||
},
|
||||
|
||||
addListener: function(eventName, fn, scope) {
|
||||
scope = scope || this;
|
||||
|
||||
if (this.eyeEvents[eventName] == undefined) {
|
||||
this.eyeEvents[eventName] = [];
|
||||
}
|
||||
|
||||
this.eyeEvents[eventName].push({
|
||||
fn: fn,
|
||||
scope: scope
|
||||
});
|
||||
},
|
||||
|
||||
removeEvent: function(eventName) {
|
||||
this.eyeEvents[eventName] = undefined;
|
||||
},
|
||||
|
||||
clearEvents: function() {
|
||||
this.eyeEvents = {};
|
||||
},
|
||||
|
||||
fire: function(eventName, params) {
|
||||
var listeners = this.eyeEvents[eventName];
|
||||
if(listeners) {
|
||||
var size = listeners.length;
|
||||
while(size--) {
|
||||
listeners[size].fn.call(listeners[size].scope, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
wdi.CHANNEL_STATUS = {
|
||||
disconnected:-1,
|
||||
idle:0,
|
||||
establishing:1,
|
||||
established:2
|
||||
};
|
||||
|
||||
wdi.Debug = {
|
||||
debug: false,
|
||||
|
||||
/* these logging functions accept multiple parameters, and will be passed
|
||||
* directly to console.{log,info,warn,error}(), so we can have better
|
||||
* messages.
|
||||
*
|
||||
* Call them with multiple params instead of concatenating:
|
||||
* YES: wdi.Debug.log("something happened: ", whatever);
|
||||
* NO : wdi.Debug.log("something happened: " + whatever);
|
||||
*/
|
||||
|
||||
log: function(variable_list_of_args /* , ... */) {
|
||||
if (this.debug) {
|
||||
console.log.apply(console, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
},
|
||||
|
||||
warn: function(variable_list_of_args /* , ... */) {
|
||||
console.warn.apply(console, Array.prototype.slice.call(arguments));
|
||||
},
|
||||
|
||||
info: function(variable_list_of_args /* , ... */) {
|
||||
if (this.debug) {
|
||||
console.info.apply(console, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
},
|
||||
|
||||
error: function(variable_list_of_args /* , ... */) {
|
||||
console.error.apply(console, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
wdi.Utils = {
|
||||
generateWebSocketUrl: function(protocol, host, port, destHost, destPort, type, destInfoToken) {
|
||||
/**
|
||||
* Generates websockify URL.
|
||||
* If destHost and destPort are available, they are used to form explicit URL with host and port.
|
||||
* If not, an URL with destInfoToken is generated, host and port are resolved by backend service.
|
||||
*/
|
||||
if ( ! destHost || ! destPort ) {
|
||||
url = `${protocol}://${window.location.host}/ws/${host}/${port}`;
|
||||
} else {
|
||||
url = protocol + '://' + host + ':' + port + '/websockify/host/' + destHost + '/port/' + destPort + '/type/' + type;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
wdi.postMessageW3CCompilant = typeof window !== "undefined" && window['bowser'] && !(window['bowser']['msie'] && window['bowser']['version'] >= 10);
|
||||
|
||||
wdi.Exception = $.spcExtend(wdi.DomainObject, {
|
||||
errorCode: null,
|
||||
message: null,
|
||||
|
||||
init: function(c) {
|
||||
this.message = c.message || '';
|
||||
this.errorCode = c.errorCode || 0;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
new ImageData(1,1);
|
||||
} catch(e) {
|
||||
if (typeof window !== 'undefined') {//Just in case it is nodejs
|
||||
window.ImageData = function(arr, width, height) {
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
var imgData = context.createImageData(width, height);
|
||||
imgData.data.set(arr);
|
||||
return imgData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wdi.bppMask = [];
|
||||
wdi.bppMask[1] = [128, 64, 32, 16, 8, 4, 2, 1];
|
||||
wdi.bppMask[4] = [240, 15];
|
||||
wdi.bppMask[8] = [255];
|
||||
|
||||
wdi.SeamlessIntegration = true;
|
||||
wdi.Debug.debug = false;
|
||||
wdi.exceptionHandling = true;
|
||||
wdi.IntegrationBenchmarkEnabled = false; // MS Excel loading time benchmark
|
||||
wdi.useWorkers = true;
|
||||
wdi.logOperations = false;
|
351
js/spice-web-client/lib/virtualjoystick.js
Normal file
351
js/spice-web-client/lib/virtualjoystick.js
Normal file
@ -0,0 +1,351 @@
|
||||
var VirtualJoystick = function(opts)
|
||||
{
|
||||
opts = opts || {};
|
||||
this._container = opts.container || document.body;
|
||||
this._stickEl = opts.stickElement || this._buildJoystickStick();
|
||||
this._baseEl = opts.baseElement || this._buildJoystickBase();
|
||||
this._mouseLeft = opts.mouseLeft || this._buildJoystickButton();
|
||||
this._mouseSupport = 'mouseSupport' in opts? opts.mouseSupport : false;
|
||||
this._range = opts.range || 60;
|
||||
|
||||
this._timer = null;
|
||||
|
||||
this._callback = 'callback' in opts? opts.callback : false;
|
||||
this._callbackcontext = 'callbackcontext' in opts? opts.callbackcontext: this;
|
||||
this._callbackButton = 'callbackbutton' in opts? opts.callbackbutton : false;
|
||||
this._referenceItem = opts.referenceItem;
|
||||
|
||||
this._container.style.position = "relative";
|
||||
|
||||
this._container.appendChild(this._baseEl);
|
||||
this._container.appendChild(this._mouseLeft);
|
||||
|
||||
this._baseEl.style.position = "absolute"
|
||||
this._baseEl.style.display = "block";
|
||||
|
||||
this._mouseLeft.style.position = "absolute"
|
||||
this._mouseLeft.style.display = "block";
|
||||
|
||||
//calculate positions
|
||||
var jcnt = $(this._referenceItem);
|
||||
var baseTop = jcnt.height()-jcnt.offset().top-130;
|
||||
var baseLeft = 20;
|
||||
var baseRight = 20;
|
||||
this._baseEl.style.top = baseTop+'px';
|
||||
this._baseEl.style.left = baseLeft+'px';
|
||||
|
||||
this._container.appendChild(this._stickEl);
|
||||
this._stickEl.style.position = "absolute"
|
||||
this._stickEl.style.display = "none";
|
||||
|
||||
this._mouseLeft.style.top = baseTop+20+'px';
|
||||
this._mouseLeft.style.right = baseRight+'px';
|
||||
|
||||
this._pressed = false;
|
||||
this._baseX = 0;
|
||||
this._baseY = 0;
|
||||
this._stickX = 0;
|
||||
this._stickY = 0;
|
||||
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
this._$onTouchStart = __bind(this._onTouchStart , this);
|
||||
this._$onTouchEnd = __bind(this._onTouchEnd , this);
|
||||
this._$onTouchMove = __bind(this._onTouchMove , this);
|
||||
this._$onTouchStartButton = __bind(this._onTouchStartButton, this);
|
||||
this._$onTouchEndButton = __bind(this._onTouchEndButton, this);
|
||||
this._baseEl.addEventListener( 'touchstart' , this._$onTouchStart , false );
|
||||
this._mouseLeft.addEventListener( 'touchstart' , this._$onTouchStartButton , false );
|
||||
this._mouseLeft.addEventListener( 'touchend' , this._$onTouchEndButton , false );
|
||||
this._container.addEventListener( 'touchend' , this._$onTouchEnd , false );
|
||||
this._container.addEventListener( 'touchmove' , this._$onTouchMove , false );
|
||||
if( this._mouseSupport ){
|
||||
this._$onMouseDown = __bind(this._onMouseDown , this);
|
||||
this._$onMouseUp = __bind(this._onMouseUp , this);
|
||||
this._$onMouseMove = __bind(this._onMouseMove , this);
|
||||
this._$onMouseDownButton = __bind(this._onMouseDownButton, this);
|
||||
this._$onMouseUpButton = __bind(this._onMouseUpButton, this);
|
||||
this._baseEl.addEventListener( 'mousedown' , this._$onMouseDown , false );
|
||||
this._container.addEventListener( 'mouseup' , this._$onMouseUp , false );
|
||||
this._mouseLeft.addEventListener( 'mousedown' , this._$onTouchStartButton , false );
|
||||
this._mouseLeft.addEventListener( 'mouseup' , this._$onTouchEndButton , false );
|
||||
this._container.addEventListener( 'mousemove' , this._$onMouseMove , false );
|
||||
}
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype.destroy = function()
|
||||
{
|
||||
this._container.removeChild(this._baseEl);
|
||||
this._container.removeChild(this._stickEl);
|
||||
|
||||
this._mouseLeft.removeEventListener( 'touchstart' , this._$onTouchStartButton , false );
|
||||
this._mouseLeft.removeEventListener( 'touchend' , this._$onTouchEndButton , false );
|
||||
this._container.removeChild(this._mouseLeft);
|
||||
|
||||
this._container.removeEventListener( 'touchstart' , this._$onTouchStart , false );
|
||||
this._container.removeEventListener( 'touchend' , this._$onTouchEnd , false );
|
||||
this._container.removeEventListener( 'touchmove' , this._$onTouchMove , false );
|
||||
if( this._mouseSupport ){
|
||||
this._container.removeEventListener( 'mouseup' , this._$onMouseUp , false );
|
||||
this._container.removeEventListener( 'mousedown' , this._$onMouseDown , false );
|
||||
this._container.removeEventListener( 'mousemove' , this._$onMouseMove , false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Boolean} true if touchscreen is currently available, false otherwise
|
||||
*/
|
||||
VirtualJoystick.touchScreenAvailable = function()
|
||||
{
|
||||
return 'createTouch' in document ? true : false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VirtualJoystick.prototype.deltaX = function(){ return this._stickX - this._baseX; }
|
||||
VirtualJoystick.prototype.deltaY = function(){ return this._stickY - this._baseY; }
|
||||
|
||||
VirtualJoystick.prototype.up = function(){
|
||||
if( this._pressed === false ) return false;
|
||||
var deltaX = this.deltaX();
|
||||
var deltaY = this.deltaY();
|
||||
if( deltaY >= 0 ) return false;
|
||||
if( Math.abs(deltaY) < this._range && Math.abs(deltaY) < Math.abs(deltaX) ){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
VirtualJoystick.prototype.down = function(){
|
||||
if( this._pressed === false ) return false;
|
||||
var deltaX = this.deltaX();
|
||||
var deltaY = this.deltaY();
|
||||
if( deltaY <= 0 ) return false;
|
||||
if( Math.abs(deltaY) < this._range && Math.abs(deltaY) < Math.abs(deltaX) ){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
VirtualJoystick.prototype.right = function(){
|
||||
if( this._pressed === false ) return false;
|
||||
var deltaX = this.deltaX();
|
||||
var deltaY = this.deltaY();
|
||||
if( deltaX <= 0 ) return false;
|
||||
if( Math.abs(deltaX) < this._range && Math.abs(deltaY) > Math.abs(deltaX) ){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
VirtualJoystick.prototype.left = function(){
|
||||
if( this._pressed === false ) return false;
|
||||
var deltaX = this.deltaX();
|
||||
var deltaY = this.deltaY();
|
||||
if( deltaX >= 0 ) return false;
|
||||
if( Math.abs(deltaX) < this._range && Math.abs(deltaY) > Math.abs(deltaX) ){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VirtualJoystick.prototype._onUp = function()
|
||||
{
|
||||
this._pressed = false;
|
||||
this._stickEl.style.display = "none";
|
||||
//this._baseEl.style.display = "none";
|
||||
|
||||
this._baseX = this._baseY = 0;
|
||||
this._stickX = this._stickY = 0;
|
||||
this._eraseInterval();
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onDown = function(x, y)
|
||||
{
|
||||
this._pressed = true;
|
||||
this._stickX = x;
|
||||
this._stickY = y;
|
||||
|
||||
this._stickEl.style.display = "";
|
||||
this._stickEl.style.left = (this._stickX - this._stickEl.width /2)+"px";
|
||||
this._stickEl.style.top = (this._stickY - this._stickEl.height/2)+"px";
|
||||
this._startInterval();
|
||||
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onMove = function(x, y)
|
||||
{
|
||||
if( this._pressed === true ){
|
||||
this._stickX = x;
|
||||
this._stickY = y;
|
||||
this._stickEl.style.left = (x - this._stickEl.width /2)+"px";
|
||||
this._stickEl.style.top = (y - this._stickEl.height/2)+"px";
|
||||
this._startInterval();
|
||||
}
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._startInterval = function(x, y) {
|
||||
this._eraseInterval();
|
||||
var centerXBase = $(this._baseEl).offset().left + ($(this._baseEl).width()/2);
|
||||
var centerXStick = $(this._stickEl).offset().left + ($(this._stickEl).width()/2);
|
||||
this.offsetX = parseInt((centerXStick - centerXBase) / 5);
|
||||
|
||||
//////
|
||||
|
||||
var centerYBase = $(this._baseEl).offset().top + ($(this._baseEl).height()/2);
|
||||
var centerYStick = $(this._stickEl).offset().top + ($(this._stickEl).height()/2);
|
||||
|
||||
this.offsetY = parseInt((centerYStick - centerYBase) / 5);
|
||||
|
||||
if(this.offsetX > 15) {
|
||||
this.offsetX = 15;
|
||||
}
|
||||
|
||||
if(this.offsetY > 15) {
|
||||
this.offsetY = 15;
|
||||
}
|
||||
//calculate movements from offsets
|
||||
|
||||
var self = this;
|
||||
this._timer = setInterval(function() {
|
||||
if(self._callback) {
|
||||
self._callback.call(self._callbackcontext, [self.offsetX, self.offsetY]);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._eraseInterval = function() {
|
||||
if(this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
this._timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// bind touch events (and mouse events for debug) //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VirtualJoystick.prototype._onMouseUp = function(event)
|
||||
{
|
||||
return this._onUp();
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onMouseDown = function(event)
|
||||
{
|
||||
var x = event.pageX;
|
||||
var y = event.pageY;
|
||||
return this._onDown(x, y);
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onMouseMove = function(event)
|
||||
{
|
||||
var x = event.pageX;
|
||||
var y = event.pageY;
|
||||
return this._onMove(x, y);
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onTouchStart = function(event)
|
||||
{
|
||||
if( event.touches.length != 1 ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var x = event.touches[ 0 ].pageX;
|
||||
var y = event.touches[ 0 ].pageY;
|
||||
return this._onDown(x, y)
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onTouchEnd = function(event)
|
||||
{
|
||||
//??????
|
||||
// no preventDefault to get click event on ios
|
||||
event.preventDefault();
|
||||
|
||||
return this._onUp()
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onTouchMove = function(event)
|
||||
{
|
||||
if(event.touches && event.touches.length != 1 ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var x = event.touches[ 0 ].pageX;
|
||||
var y = event.touches[ 0 ].pageY;
|
||||
return this._onMove(x, y)
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onTouchStartButton = function(event)
|
||||
{
|
||||
if( event.touches && event.touches.length != 1 ) return;
|
||||
|
||||
event.preventDefault();
|
||||
this._callbackButton.call(this._callbackcontext, ['down']);
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._onTouchEndButton = function(event)
|
||||
{
|
||||
//??????
|
||||
// no preventDefault to get click event on ios
|
||||
event.preventDefault();
|
||||
this._callbackButton.call(this._callbackcontext, ['up']);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// build default stickEl and baseEl //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VirtualJoystick.prototype._buildJoystickBase = function()
|
||||
{
|
||||
var canvas = document.createElement( 'canvas' );
|
||||
canvas.style.zIndex = 2;
|
||||
canvas.width = 126;
|
||||
canvas.height = 126;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "cyan";
|
||||
ctx.lineWidth = 6;
|
||||
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "cyan";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.arc( canvas.width/2, canvas.width/2, 60, 0, Math.PI*2, true);
|
||||
ctx.stroke();
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._buildJoystickStick = function()
|
||||
{
|
||||
var canvas = document.createElement( 'canvas' );
|
||||
canvas.style.zIndex = 2;
|
||||
canvas.width = 86;
|
||||
canvas.height = 86;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "cyan";
|
||||
ctx.lineWidth = 6;
|
||||
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
|
||||
ctx.stroke();
|
||||
return canvas;
|
||||
}
|
||||
|
||||
VirtualJoystick.prototype._buildJoystickButton = function()
|
||||
{
|
||||
var canvas = document.createElement( 'canvas' );
|
||||
canvas.style.zIndex = 2;
|
||||
canvas.width = 86;
|
||||
canvas.height = 86;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.lineWidth = 6;
|
||||
ctx.arc( canvas.width/2, canvas.width/2, 40, 0, Math.PI*2, true);
|
||||
ctx.stroke();
|
||||
return canvas;
|
||||
}
|
208
js/spice-web-client/network/busconnection.js
Normal file
208
js/spice-web-client/network/busconnection.js
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.BusConnection = $.spcExtend(wdi.EventObject.prototype, {
|
||||
ws: null,
|
||||
subscriptions: [],
|
||||
_busUser: null,
|
||||
_busPass: null,
|
||||
fileServerBaseUrl: null,
|
||||
queue: '',
|
||||
binary: false,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.ws = c.websocket || new wdi.WebSocketWrapper();
|
||||
this.clusterNodeChooser = c.clusterNodeChooser || new wdi.ClusterNodeChooser();
|
||||
this.binary = c.binary || false;
|
||||
},
|
||||
|
||||
connect: function(c) {
|
||||
if (!c['useBus']) {
|
||||
wdi.Debug.warn("Not using the bus");
|
||||
return;
|
||||
}
|
||||
this._vdiBusToken = c['vdiBusToken'];
|
||||
if (!c['busHostList']) {
|
||||
wdi.Debug.warn("Deprecated: using old busHost & busPort params");
|
||||
c['busHostList'] = [{
|
||||
host: c['busHost'],
|
||||
port: c['busPort']
|
||||
}];
|
||||
}
|
||||
this.clusterNodeChooser.setNodeList(c['busHostList']);
|
||||
if (Modernizr['websocketsbinary']) {
|
||||
this.binary = true;
|
||||
}
|
||||
this._busUser = c['busUser'];
|
||||
this._busPass = c['busPass'];
|
||||
this._websockifyProtocol = c['protocol'];
|
||||
this._websockifyHost = c['host'];
|
||||
this._websockifyPort = c['port'];
|
||||
this.fileServerBaseUrl = c['busFileServerBaseUrl'];
|
||||
this.subscriptions = c['busSubscriptions'];
|
||||
|
||||
this._connectToNextHost();
|
||||
},
|
||||
|
||||
_connectToNextHost: function () {
|
||||
var busData = this.clusterNodeChooser.getAnother();
|
||||
|
||||
// c['protocol'] is the protocol we use to connect to websockify
|
||||
// ie: ws, wss, https, ...
|
||||
var url = wdi.Utils.generateWebSocketUrl(
|
||||
this._websockifyProtocol,
|
||||
this._websockifyHost,
|
||||
this._websockifyPort,
|
||||
busData.host,
|
||||
busData.port,
|
||||
'raw',
|
||||
this._vdiBusToken
|
||||
);
|
||||
var websocketProtocol = 'base64';
|
||||
if (this.binary) {
|
||||
websocketProtocol = 'binary';
|
||||
}
|
||||
this.ws.connect(url, websocketProtocol);
|
||||
|
||||
wdi.Debug.log("BusConnection: using protocol: " + websocketProtocol);
|
||||
|
||||
if (this.binary) {
|
||||
this.ws.setBinaryType('arraybuffer');
|
||||
}
|
||||
this.setListeners();
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
this.ws.close();
|
||||
},
|
||||
|
||||
setListeners: function() {
|
||||
var self = this;
|
||||
this.ws.onOpen(function(e) {
|
||||
self._send("CONNECT\nlogin:" + self._busUser + "\npasscode:" + self._busPass + "\n\n\x00");
|
||||
});
|
||||
|
||||
this.ws.onMessage(function(e) {
|
||||
var message;
|
||||
var result;
|
||||
if (!self.binary) {
|
||||
message = Base64.decodeStr(e.data);
|
||||
} else {
|
||||
message = String.fromCharCode.apply(null, new Uint8Array(e.data));
|
||||
// Fix accented chars
|
||||
// [ http://stackoverflow.com/questions/5396560/how-do-i-convert-special-utf-8-chars-to-their-iso-8859-1-equivalent-using-javasc ]
|
||||
message = decodeURIComponent(escape(message));
|
||||
}
|
||||
var subChunks = message.split("\0");
|
||||
if (subChunks.length == 1) {
|
||||
// there is no \0 in the full message, add it to the queue
|
||||
self.queue += subChunks[0];
|
||||
} else {
|
||||
// at least one \0, process all but the last subchunk (that has no \0)
|
||||
for (var i = 0; i < subChunks.length - 1; i++) {
|
||||
message = self.queue + subChunks[i];
|
||||
result = self.parseMessage(message);
|
||||
self.fire('busMessage', result);
|
||||
self.queue = '';
|
||||
}
|
||||
// last chunk is now the queue
|
||||
self.queue = subChunks[subChunks.length - 1];
|
||||
}
|
||||
});
|
||||
|
||||
this.ws.onClose(function(e) {
|
||||
wdi.Debug.log('BusConnection CLOSED! connecting again in 1 second');
|
||||
self.fire('error', e);
|
||||
});
|
||||
|
||||
this.ws.onError(function(e) {
|
||||
wdi.Debug.error('BusConnection ERROR:', e);
|
||||
});
|
||||
},
|
||||
|
||||
parseMessage: function(message) {
|
||||
try {
|
||||
var arr = message.split("\n\n");
|
||||
var header = arr[0].trim();
|
||||
var body = arr[1].replace(/\x00/, '').trim();
|
||||
if (body.length != 0) {
|
||||
// there is content, so convert to object
|
||||
body = JSON.parse(body);
|
||||
} else {
|
||||
body = null;
|
||||
}
|
||||
arr = header.split("\n");
|
||||
var verb = arr.shift();
|
||||
header = "{";
|
||||
var len = arr.length;
|
||||
for (var i = 0;i < len;i++) {
|
||||
var headerName = arr[i].split(':')[0];
|
||||
header += '"' + headerName + '":"' + arr[i].replace(headerName + ':', '') + '"';
|
||||
if (i != len-1) {
|
||||
header += ",";
|
||||
}
|
||||
}
|
||||
header += "}";
|
||||
|
||||
return {'verb':verb, 'header':JSON.parse(header), 'body':body};
|
||||
} catch (e) {
|
||||
wdi.Debug.error("Error parsing Bus Info: ", e);
|
||||
return {"verb":"ERROR"};
|
||||
}
|
||||
},
|
||||
|
||||
setSubscriptions: function() {
|
||||
var len = this.subscriptions.length;
|
||||
for (var i = 0; i < len;i++) {
|
||||
this.subscribe(this.subscriptions[i]);
|
||||
}
|
||||
},
|
||||
|
||||
send: function(message) {
|
||||
var destination = this.subscriptions[0];
|
||||
this._send("SEND\ndestination:" + destination + "\ncontent-type:text/plain\n\n" + JSON.stringify(message) + "\x00");
|
||||
},
|
||||
|
||||
subscribe: function(destination) {
|
||||
//header browser: true for queue's to multiple subscribers
|
||||
this._send("SUBSCRIBE\ndestination:" + destination + "\n\n\x00");
|
||||
},
|
||||
|
||||
_send: function(message) {
|
||||
if (!this.binary) {
|
||||
this.ws.send(Base64.encodeStr(message));
|
||||
} else {
|
||||
this.ws.send(message);
|
||||
}
|
||||
}
|
||||
});
|
62
js/spice-web-client/network/clusternodechooser.js
Normal file
62
js/spice-web-client/network/clusternodechooser.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.ClusterNodeChooser = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function (c) {
|
||||
},
|
||||
|
||||
setNodeList: function (nodeList) {
|
||||
this._nodeList = this._shuffle(nodeList);
|
||||
this._nodeListLength = this._nodeList.length;
|
||||
this._currentIndex = 0;
|
||||
},
|
||||
|
||||
getAnother: function () {
|
||||
var toReturn = this._nodeList[this._currentIndex++ % this._nodeListLength];
|
||||
return toReturn;
|
||||
},
|
||||
|
||||
// recipe from: http://stackoverflow.com/a/6274398
|
||||
_shuffle: function (list) {
|
||||
var counter = list.length,
|
||||
temp,
|
||||
index;
|
||||
while (counter > 0) {
|
||||
index = Math.floor(Math.random() * counter);
|
||||
counter--;
|
||||
temp = list[counter];
|
||||
list[counter] = list[index];
|
||||
list[index] = temp;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
});
|
69
js/spice-web-client/network/connectioncontrol.js
Normal file
69
js/spice-web-client/network/connectioncontrol.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.ConnectionControl = $.spcExtend(wdi.EventObject.prototype, {
|
||||
socket: null,
|
||||
pendingTimeToConnectionLost: null,
|
||||
previousTimeOut: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.socket = c.socket || new wdi.Socket();
|
||||
},
|
||||
|
||||
connect: function(c) {
|
||||
var url = wdi.Utils.generateWebSocketUrl(c.protocol, c.host, c.port, null, null,'raw', c.heartbeatToken);
|
||||
this.socket.connect(url);
|
||||
this.pendingTimeToConnectionLost = c.heartbeatTimeout;
|
||||
wdi.Debug.log('ConnectionControl: connected');
|
||||
this.setListeners();
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
if(this.previousTimeOut){
|
||||
clearTimeout(this.previousTimeOut);
|
||||
}
|
||||
this.socket.disconnect();
|
||||
},
|
||||
|
||||
setListeners: function() {
|
||||
var self = this;
|
||||
this.socket.setOnMessageCallback(function(e) {
|
||||
wdi.Debug.log('ConectionControl: beat');
|
||||
clearTimeout(self.previousTimeOut);
|
||||
self.previousTimeOut = setTimeout(function() {
|
||||
wdi.Debug.log('ConnectionControl: firing connectionLost event');
|
||||
self.fire('connectionLost', e);
|
||||
}, self.pendingTimeToConnectionLost);
|
||||
});
|
||||
}
|
||||
});
|
67
js/spice-web-client/network/packetcontroller.js
Normal file
67
js/spice-web-client/network/packetcontroller.js
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketController = $.spcExtend(wdi.EventObject.prototype, {
|
||||
sizeDefiner: null,
|
||||
packetExtractor: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.sizeDefiner = c.sizeDefiner;
|
||||
this.packetExtractor = c.packetExtractor;
|
||||
},
|
||||
|
||||
getNextPacket: function(data) {
|
||||
var self = this;
|
||||
if (wdi.logOperations) {
|
||||
wdi.DataLogger.setNetworkTimeStart();
|
||||
}
|
||||
var size = this.sizeDefiner.getSize(data);
|
||||
this.packetExtractor.getBytes(size, function(bytes) {
|
||||
var status = this.sizeDefiner.getStatus();
|
||||
|
||||
this.execute(new wdi.RawMessage({status: status, data: bytes}));
|
||||
|
||||
self.getNextPacket(bytes);
|
||||
|
||||
|
||||
}, this);
|
||||
},
|
||||
|
||||
execute: function(message) {
|
||||
try {
|
||||
this.fire('chunkComplete', message);
|
||||
} catch (e) {
|
||||
console.error('PacketTroller: ', e);
|
||||
}
|
||||
}
|
||||
});
|
77
js/spice-web-client/network/packetextractor.js
Normal file
77
js/spice-web-client/network/packetextractor.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketExtractor = $.spcExtend(wdi.EventObject.prototype, {
|
||||
socketQ: null,
|
||||
numBytes: null,
|
||||
callback: null,
|
||||
scope: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.socketQ = c.socketQ;
|
||||
this.setListener();
|
||||
},
|
||||
|
||||
setListener: function() {
|
||||
this.socketQ.addListener('message', function() {
|
||||
if (wdi.logOperations) {
|
||||
wdi.DataLogger.setNetworkTimeStart();
|
||||
}
|
||||
this.getBytes(this.numBytes, this.callback, this.scope);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getBytes: function(numBytes, callback, scope) {
|
||||
var retLength = this.socketQ.rQ.getLength();
|
||||
this.numBytes = numBytes;
|
||||
this.callback = callback;
|
||||
this.scope = scope;
|
||||
|
||||
if (numBytes !== null && retLength >= numBytes) {
|
||||
var ret;
|
||||
if (numBytes) {
|
||||
ret = this.socketQ.rQ.shift(numBytes);
|
||||
} else {
|
||||
ret = new Uint8Array(0);
|
||||
}
|
||||
this.numBytes = null;
|
||||
this.callback = null;
|
||||
this.scope = null;
|
||||
callback.call(scope, ret);
|
||||
} else {
|
||||
if (wdi.logOperations) {
|
||||
wdi.DataLogger.logNetworkTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
77
js/spice-web-client/network/packetlinkfactory.js
Normal file
77
js/spice-web-client/network/packetlinkfactory.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketLinkFactory = {
|
||||
extract: function(header, queue) {
|
||||
switch (header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_SET_ACK:
|
||||
return new wdi.RedSetAck().demarshall(queue);
|
||||
case wdi.SpiceVars.SPICE_MSG_PING:
|
||||
return new wdi.RedPing().demarshall(queue, header.size);
|
||||
case wdi.SpiceVars.SPICE_MSG_MIGRATE:
|
||||
return new wdi.RedMigrate().demarshall(queue);
|
||||
case wdi.SpiceVars.SPICE_MSG_MIGRATE_DATA:
|
||||
return new wdi.RedMigrateData().demarshall(queue, header.size);
|
||||
case wdi.SpiceVars.SPICE_MSG_WAIT_FOR_CHANNELS:
|
||||
return new wdi.RedWaitForChannels().demarshall(queue);
|
||||
case wdi.SpiceVars.SPICE_MSG_DISCONNECTING:
|
||||
return new wdi.RedDisconnect().demarshall(queue);
|
||||
case wdi.SpiceVars.SPICE_MSG_NOTIFY:
|
||||
var packet = new wdi.RedNotify().demarshall(queue);
|
||||
return packet;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_MOUSE_MODE:
|
||||
return new wdi.SpiceMouseMode().demarshall(queue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wdi.PacketLinkProcess = {
|
||||
process: function(header, packet, channel) {
|
||||
switch(header.type) {
|
||||
case wdi.SpiceVars.SPICE_MSG_SET_ACK:
|
||||
var body = wdi.SpiceObject.numberTo32(packet.generation);
|
||||
channel.setAckWindow(packet.window)
|
||||
channel.sendObject(body, wdi.SpiceVars.SPICE_MSGC_ACK_SYNC);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PING:
|
||||
var body = new wdi.RedPing({id: packet.id, time: packet.time}).marshall();
|
||||
channel.sendObject(body, wdi.SpiceVars.SPICE_MSGC_PONG);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_MOUSE_MODE:
|
||||
channel.fire('mouseMode', packet.current_mode);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_NOTIFY:
|
||||
channel.fire('notify');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
80
js/spice-web-client/network/packetreassembler.js
Normal file
80
js/spice-web-client/network/packetreassembler.js
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.PacketReassembler = $.spcExtend(wdi.EventObject.prototype, {
|
||||
packetController: null,
|
||||
currentHeader: null,
|
||||
statusToString: null,
|
||||
sizeDefinerConstant: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.packetController = c.packetController;
|
||||
this.sizeDefinerConstant = wdi.SizeDefiner.prototype;
|
||||
this.statusToString = [];
|
||||
this.statusToString[this.sizeDefinerConstant.STATUS_REPLY_BODY] = 'reply';
|
||||
this.statusToString[this.sizeDefinerConstant.STATUS_ERROR_CODE] = 'errorCode';
|
||||
this.statusToString[this.sizeDefinerConstant.STATUS_BODY] = 'spicePacket';
|
||||
this.setListeners();
|
||||
|
||||
},
|
||||
|
||||
start: function () {
|
||||
this.packetController.getNextPacket();
|
||||
},
|
||||
|
||||
setListeners: function() {
|
||||
this.packetController.addListener('chunkComplete', function(e) {
|
||||
var rawMessage = e;
|
||||
var status = rawMessage.status;
|
||||
switch(status) {
|
||||
case this.sizeDefinerConstant.STATUS_HEADER:
|
||||
case this.sizeDefinerConstant.STATUS_REPLY:
|
||||
this.currentHeader = rawMessage;
|
||||
break;
|
||||
case this.sizeDefinerConstant.STATUS_REPLY_BODY:
|
||||
case this.sizeDefinerConstant.STATUS_BODY:
|
||||
var tmpBuff = new Uint8Array(rawMessage.data.length + this.currentHeader.data.length);
|
||||
tmpBuff.set(this.currentHeader.data);
|
||||
tmpBuff.set(rawMessage.data, this.currentHeader.data.length);
|
||||
rawMessage.data = tmpBuff;
|
||||
rawMessage.status = this.statusToString[status];
|
||||
this.fire('packetComplete', rawMessage);
|
||||
break;
|
||||
default:
|
||||
rawMessage.status = this.statusToString[status];
|
||||
this.fire('packetComplete', rawMessage);
|
||||
break;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
52
js/spice-web-client/network/reassemblerfactory.js
Normal file
52
js/spice-web-client/network/reassemblerfactory.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.ReassemblerFactory = {
|
||||
getPacketReassembler: function(socketQ) {
|
||||
var pE = this.getPacketExtractor(socketQ);
|
||||
var sD = this.getSizeDefiner();
|
||||
var pC = this.getPacketController(pE, sD);
|
||||
return new wdi.PacketReassembler({packetController: pC});
|
||||
},
|
||||
|
||||
getPacketExtractor: function(socketQ) {
|
||||
return new wdi.PacketExtractor({socketQ: socketQ});
|
||||
},
|
||||
|
||||
getSizeDefiner: function() {
|
||||
return new wdi.SizeDefiner();
|
||||
},
|
||||
|
||||
getPacketController: function(packetExtractor, sizeDefiner) {
|
||||
return new wdi.PacketController({packetExtractor: packetExtractor, sizeDefiner: sizeDefiner});
|
||||
}
|
||||
};
|
99
js/spice-web-client/network/sizedefiner.js
Normal file
99
js/spice-web-client/network/sizedefiner.js
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SizeDefiner = $.spcExtend(wdi.DomainObject, {
|
||||
ERROR_CODE_SIZE: 4,
|
||||
status: null,
|
||||
STATUS_READY: 0,
|
||||
STATUS_REPLY: 1,
|
||||
STATUS_REPLY_BODY: 2,
|
||||
STATUS_ERROR_CODE: 3,
|
||||
STATUS_MESSAGE: 4,
|
||||
STATUS_HEADER: 5,
|
||||
STATUS_BODY: 6,
|
||||
isHeader: false,
|
||||
|
||||
init: function(c) {
|
||||
this.status = this.STATUS_READY;
|
||||
},
|
||||
|
||||
getSize: function(arr) {
|
||||
if (this.STATUS_READY === this.status) {
|
||||
this.status++;
|
||||
return wdi.SpiceLinkHeader.prototype.objectSize;
|
||||
} else if (this.STATUS_REPLY === this.status) {
|
||||
this.status++;
|
||||
return this.getReplyBodySize(arr);
|
||||
} else if (this.STATUS_REPLY_BODY === this.status) {
|
||||
this.status++;
|
||||
return this.ERROR_CODE_SIZE;
|
||||
} else if (this.STATUS_ERROR_CODE === this.status) {
|
||||
this.status++;
|
||||
this.isHeader = true;
|
||||
return 6; //wdi.SpiceDataHeader.prototype.objectSize access here is slow
|
||||
} else {
|
||||
if (this.isHeader) {
|
||||
this.isHeader = false;
|
||||
return this.getBodySizeFromArrayHeader(arr);
|
||||
} else {
|
||||
this.isHeader = true;
|
||||
return 6;//wdi.SpiceDataHeader.prototype.objectSize; access here is slow
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getReplyBodySize: function (arr) {
|
||||
var queue = wdi.GlobalPool.create('ViewQueue');
|
||||
queue.setData(arr);
|
||||
var header = new wdi.SpiceLinkHeader().demarshall(queue);
|
||||
wdi.GlobalPool.discard('ViewQueue', queue);
|
||||
return header.size;
|
||||
},
|
||||
|
||||
getBodySizeFromArrayHeader: function (arr) {
|
||||
var queue = wdi.GlobalPool.create('ViewQueue');
|
||||
queue.setData(arr);
|
||||
var header = new wdi.SpiceDataHeader().demarshall(queue);
|
||||
wdi.GlobalPool.discard('ViewQueue', queue);
|
||||
return header.size;
|
||||
},
|
||||
|
||||
getStatus: function() {
|
||||
if (this.status === this.STATUS_MESSAGE && this.isHeader) {
|
||||
return this.STATUS_HEADER;
|
||||
} else if (this.status === this.STATUS_MESSAGE && !this.isHeader) {
|
||||
return this.STATUS_BODY;
|
||||
} else {
|
||||
return this.status;
|
||||
}
|
||||
}
|
||||
});
|
131
js/spice-web-client/network/socket.js
Normal file
131
js/spice-web-client/network/socket.js
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.socketStatus = {
|
||||
'idle':0,
|
||||
'prepared':1,
|
||||
'connected':2,
|
||||
'disconnected':3,
|
||||
'failed':4
|
||||
};
|
||||
//Works only with arrays of bytes (this means each value is a number in 0 to 255)
|
||||
wdi.Socket = $.spcExtend(wdi.EventObject.prototype, {
|
||||
websocket: null,
|
||||
status: wdi.socketStatus.idle,
|
||||
binary: false,
|
||||
|
||||
connect: function(uri) {
|
||||
var self = this;
|
||||
var protocol = 'base64'; //default protocol
|
||||
|
||||
if(Modernizr['websocketsbinary']) {
|
||||
protocol = 'binary';
|
||||
this.binary = true;
|
||||
}
|
||||
|
||||
this.websocket = new WebSocket(uri, protocol);
|
||||
|
||||
wdi.Debug.log("Socket: using protocol: "+protocol);
|
||||
|
||||
if(this.binary) {
|
||||
this.websocket.binaryType = 'arraybuffer';
|
||||
}
|
||||
|
||||
this.status = wdi.socketStatus.prepared;
|
||||
this.websocket.onopen = function() {
|
||||
self.status = wdi.socketStatus.connected;
|
||||
self.fire('open');
|
||||
};
|
||||
this.websocket.onmessage = function(e) {
|
||||
self.fire('message', e.data);
|
||||
};
|
||||
this.websocket.onclose = function(e) {
|
||||
self.status = wdi.socketStatus.disconnected;
|
||||
console.warn('Spice Web Client: ', e.code, e.reason);
|
||||
self.disconnect();
|
||||
self.fire('error', e);
|
||||
};
|
||||
this.websocket.onerror = function(e) {
|
||||
self.status = wdi.socketStatus.failed;
|
||||
self.fire('error', e);
|
||||
};
|
||||
},
|
||||
|
||||
setOnMessageCallback: function(callback) {
|
||||
this.websocket.onmessage = callback;
|
||||
},
|
||||
|
||||
send: function(message) {
|
||||
try {
|
||||
this.websocket.send(this.encode_message(message));
|
||||
} catch (err) {
|
||||
this.status = wdi.socketStatus.failed;
|
||||
this.fire('error', err);
|
||||
}
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
if (this.websocket) {
|
||||
this.websocket.onopen = function() {};
|
||||
this.websocket.onmessage = function() {};
|
||||
this.websocket.onclose = function() {};
|
||||
this.websocket.onerror = function() {};
|
||||
this.websocket.close();
|
||||
this.websocket = null;
|
||||
}
|
||||
},
|
||||
|
||||
setStatus: function(status) {
|
||||
this.status = status;
|
||||
this.fire('status', status);
|
||||
},
|
||||
|
||||
getStatus: function() {
|
||||
return this.status;
|
||||
},
|
||||
|
||||
encode_message: function(mess) {
|
||||
if(!this.binary) {
|
||||
var arr = Base64.encode(mess);
|
||||
return arr;
|
||||
}
|
||||
|
||||
var len = mess.length;
|
||||
|
||||
var buffer = new ArrayBuffer(len);
|
||||
var u8 = new Uint8Array(buffer);
|
||||
|
||||
u8.set(mess);
|
||||
|
||||
return u8;
|
||||
}
|
||||
});
|
98
js/spice-web-client/network/socketqueue.js
Normal file
98
js/spice-web-client/network/socketqueue.js
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SocketQueue = $.spcExtend(wdi.EventObject.prototype, {
|
||||
rQ: null,
|
||||
sQ: null,
|
||||
socket: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.socket = c.socket || new wdi.Socket();
|
||||
this.rQ = c.rQ || new wdi.FixedQueue();
|
||||
this.sQ = c.sQ || new wdi.Queue();
|
||||
this.setup();
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
this.socket.addListener('open', function() {
|
||||
this.fire('open');
|
||||
}, this);
|
||||
this.socket.addListener('message', function(data) {
|
||||
this.rQ.push(new Uint8Array(data));
|
||||
this.fire('message');
|
||||
}, this);
|
||||
this.socket.addListener('close', function(e) {
|
||||
this.fire('close', e);
|
||||
}, this);
|
||||
this.socket.addListener('error', function(e) {
|
||||
this.fire('error', e);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getStatus: function() {
|
||||
return this.socket.getStatus();
|
||||
},
|
||||
|
||||
connect: function(uri) {
|
||||
this.socket.connect(uri);
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
this.socket.disconnect();
|
||||
},
|
||||
|
||||
send: function(data, shouldFlush) {
|
||||
//check for shouldFlush parameter, by default is true
|
||||
if (shouldFlush === undefined) {
|
||||
var flush = true;
|
||||
} else {
|
||||
var flush = shouldFlush;
|
||||
}
|
||||
|
||||
//performance: avoid passing through the queue if there is no queue and
|
||||
//we have flush!
|
||||
if(this.sQ.getLength() == 0 && flush) {
|
||||
this.socket.send(data);
|
||||
return;
|
||||
}
|
||||
|
||||
//normal operation, append to buffer and send if flush
|
||||
this.sQ.push(data);
|
||||
if (flush) this.flush();
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
var data = this.sQ.shift();
|
||||
this.socket.send(data);
|
||||
}
|
||||
});
|
226
js/spice-web-client/network/spicechannel.js
Normal file
226
js/spice-web-client/network/spicechannel.js
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
//Must fire event types: connectionId and message
|
||||
|
||||
wdi.SpiceChannel = $.spcExtend(wdi.EventObject.prototype, {
|
||||
counter: 0,
|
||||
ackWindow: 0,
|
||||
connectionId: 0,
|
||||
socketQ: null,
|
||||
packetReassembler: null,
|
||||
channel: 1,
|
||||
proxy: null,
|
||||
token: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.socketQ = c.socketQ || new wdi.SocketQueue();
|
||||
this.packetReassembler = c.packetReassembler || wdi.ReassemblerFactory.getPacketReassembler(this.socketQ);
|
||||
this.setListeners();
|
||||
this.ackWindow = 0;
|
||||
},
|
||||
|
||||
setListeners: function() {
|
||||
var date;
|
||||
this.packetReassembler.addListener('packetComplete', function(e) {
|
||||
var rawMessage = e;
|
||||
if (rawMessage.status === 'spicePacket') {
|
||||
if (wdi.logOperations) {
|
||||
wdi.DataLogger.logNetworkTime();
|
||||
date = Date.now();
|
||||
}
|
||||
var rsm = this.getRawSpiceMessage(rawMessage.data);
|
||||
if (rsm) {
|
||||
if (wdi.logOperations && rsm.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY) {
|
||||
wdi.DataLogger.setStartTime(date);
|
||||
}
|
||||
this.fire('message', rsm);
|
||||
}
|
||||
} else if (rawMessage.status === 'reply') {
|
||||
var packet = this.getRedLinkReplyBytes(rawMessage.data);
|
||||
this.send(packet);
|
||||
} else if (rawMessage.status === 'errorCode') {
|
||||
var packet = this.getErrorCodeBytes(rawMessage.data);
|
||||
if (packet) {
|
||||
this.send(packet);
|
||||
}
|
||||
this.fire('channelConnected');
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.socketQ.addListener('open', function() {
|
||||
var packet = this.getRedLinkMessBytes();
|
||||
this.send(packet);
|
||||
this.proxy ? this.proxy.end() : false;
|
||||
}, this);
|
||||
|
||||
this.socketQ.addListener('close', function(e) {
|
||||
if (this.channel === 1) {
|
||||
this.fire('error', e);
|
||||
}
|
||||
this.socketQ.disconnect();
|
||||
}, this);
|
||||
|
||||
this.socketQ.addListener('error', function() {
|
||||
this.fire('error', 3);
|
||||
this.socketQ.disconnect();
|
||||
// throw new wdi.Exception({message:"Socket error", errorCode: 2});
|
||||
}, this);
|
||||
},
|
||||
|
||||
connect: function(connectionInfo, channel, connectionId, proxy) {
|
||||
var url = wdi.Utils.generateWebSocketUrl(connectionInfo.protocol, connectionInfo.host, connectionInfo.port, connectionInfo.vmHost, connectionInfo.vmPort, 'spice', connectionInfo.vmInfoToken);
|
||||
this.channel = channel;
|
||||
this.connectionId = connectionId || 0;
|
||||
this.socketQ.connect(url);
|
||||
this.proxy = proxy;
|
||||
this.token = connectionInfo.token;
|
||||
this.packetReassembler.start();
|
||||
},
|
||||
|
||||
disconnect: function () {
|
||||
this.socketQ.disconnect();
|
||||
},
|
||||
|
||||
send: function(data, flush) {
|
||||
this.socketQ.send(data, flush);
|
||||
},
|
||||
|
||||
sendObject: function(data, type, flush) {
|
||||
var packet = new wdi.SpiceDataHeader({
|
||||
type:type,
|
||||
size:data.length
|
||||
}).marshall();
|
||||
|
||||
packet = packet.concat(data);
|
||||
|
||||
this.send(packet, flush);
|
||||
},
|
||||
|
||||
setAckWindow: function(window) {
|
||||
this.ackWindow = window;
|
||||
this.counter = 0;
|
||||
},
|
||||
|
||||
getRawSpiceMessage: function (rawData) {
|
||||
var headerQueue = wdi.GlobalPool.create('ViewQueue');
|
||||
var body = wdi.GlobalPool.create('ViewQueue');
|
||||
|
||||
var header = new Uint8Array(rawData, 0, wdi.SpiceDataHeader.prototype.objectSize);
|
||||
headerQueue.setData(header);
|
||||
var headerObj = new wdi.SpiceDataHeader().demarshall(headerQueue);
|
||||
wdi.GlobalPool.discard('ViewQueue', headerQueue);
|
||||
var rawBody = rawData.subarray(wdi.SpiceDataHeader.prototype.objectSize);
|
||||
body.setData(rawBody);
|
||||
|
||||
this.counter++;
|
||||
|
||||
if(this.ackWindow && this.counter === this.ackWindow) {
|
||||
this.counter = 0;
|
||||
var ack = new wdi.SpiceDataHeader({
|
||||
type: wdi.SpiceVars.SPICE_MSGC_ACK,
|
||||
size:0
|
||||
}).marshall();
|
||||
this.send(ack);
|
||||
}
|
||||
|
||||
var packet = wdi.PacketLinkFactory.extract(headerObj, body) || false;
|
||||
if (packet) {
|
||||
wdi.PacketLinkProcess.process(headerObj, packet, this);
|
||||
wdi.GlobalPool.discard('ViewQueue', body);
|
||||
return false;
|
||||
} else {
|
||||
var rawSpiceMessage = wdi.GlobalPool.create('RawSpiceMessage');
|
||||
rawSpiceMessage.set(headerObj, body, this.channel);
|
||||
return rawSpiceMessage;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//This functions are to avoid hardcoded values on logic
|
||||
getRedLinkReplyBytes: function(data) {
|
||||
if (this.token) {
|
||||
var newq = new wdi.ViewQueue();
|
||||
newq.setData(data);
|
||||
newq.eatBytes(wdi.SpiceLinkHeader.prototype.objectSize)
|
||||
var myBody = new wdi.SpiceLinkReply().demarshall(newq);
|
||||
|
||||
//Returnnig void bytes or encrypted ticket
|
||||
var key = wdi.SpiceObject.stringHexToBytes(RSA_public_encrypt(this.token, myBody.pub_key));
|
||||
return key;
|
||||
} else {
|
||||
return wdi.SpiceObject.stringToBytesPadding('', 128);
|
||||
}
|
||||
},
|
||||
|
||||
getRedLinkMessBytes: function() {
|
||||
var header = new wdi.SpiceLinkHeader({magic:1363428690, major_version:2, minor_version:2, size:22}).marshall();
|
||||
var body = new wdi.SpiceLinkMess({
|
||||
connection_id:this.connectionId,
|
||||
channel_type:this.channel,
|
||||
caps_offset:18,
|
||||
num_common_caps: 1,
|
||||
common_caps: (1 << wdi.SpiceVars.SPICE_COMMON_CAP_MINI_HEADER)
|
||||
}).marshall();
|
||||
return header.concat(body);
|
||||
},
|
||||
|
||||
getErrorCodeBytes: function (data) {
|
||||
var errorQ = wdi.GlobalPool.create('ViewQueue');
|
||||
errorQ.setData(data);
|
||||
var errorCode = wdi.SpiceObject.bytesToInt32NoAllocate(errorQ);
|
||||
wdi.GlobalPool.discard('ViewQueue', errorQ);
|
||||
if (errorCode === 0) {
|
||||
if (this.channel === wdi.SpiceVars.SPICE_CHANNEL_DISPLAY) {
|
||||
var redDisplayInit = new wdi.SpiceDataHeader({type: wdi.SpiceVars.SPICE_MSGC_DISPLAY_INIT, size: 14}).marshall();
|
||||
//TODO: ultrahardcoded value here, move to configuration
|
||||
|
||||
//DUE To high level storage the memory specified for cache
|
||||
//is 2-3 times bigger than expected.
|
||||
var cache_size = 0*1024*1024;
|
||||
|
||||
var body = new wdi.SpiceCDisplayInit({
|
||||
pixmap_cache_id:1,
|
||||
pixmap_cache_size: cache_size,
|
||||
glz_dictionary_id: 0,
|
||||
glz_dictionary_window_size: 1
|
||||
}).marshall();
|
||||
|
||||
return redDisplayInit.concat(body);
|
||||
} else if(this.channel == wdi.SpiceVars.SPICE_CHANNEL_MAIN) {
|
||||
return new wdi.SpiceDataHeader({type: wdi.SpiceVars.SPICE_MSGC_MAIN_ATTACH_CHANNELS, size: 0}).marshall();
|
||||
}
|
||||
} else {
|
||||
throw new wdi.Exception({message: "Server refused client", errorCode: 2});
|
||||
}
|
||||
}
|
||||
});
|
88
js/spice-web-client/network/websocketwrapper.js
Normal file
88
js/spice-web-client/network/websocketwrapper.js
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.WebSocketWrapper = $.spcExtend({}, {
|
||||
ws: {},
|
||||
onopen: null,
|
||||
onmessage: null,
|
||||
onclose: null,
|
||||
onerror: null,
|
||||
|
||||
init: function() {
|
||||
|
||||
},
|
||||
|
||||
connect: function(url, protocol) {
|
||||
this.ws = new WebSocket(url, protocol);
|
||||
},
|
||||
|
||||
onOpen: function(callback) {
|
||||
this.ws.onopen = callback;
|
||||
},
|
||||
|
||||
onMessage: function(callback) {
|
||||
this.ws.onmessage = callback;
|
||||
},
|
||||
|
||||
onClose: function(callback) {
|
||||
this.ws.onclose = callback;
|
||||
},
|
||||
|
||||
onError: function(callback) {
|
||||
this.ws.onerror = callback;
|
||||
},
|
||||
|
||||
setBinaryType: function(type) {
|
||||
this.ws.binaryType = type;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (!this.ws || !this.ws.close) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ws.close();
|
||||
this.ws.onopen = function () {};
|
||||
this.ws.onmessage = function () {};
|
||||
this.ws.onclose = function () {};
|
||||
this.ws.onerror = function () {};
|
||||
this.onopen = function() {};
|
||||
this.onmessage = function() {};
|
||||
this.onclose = function() {};
|
||||
this.onerror = function() {};
|
||||
|
||||
},
|
||||
|
||||
send: function(message) {
|
||||
this.ws.send(message);
|
||||
}
|
||||
});
|
16
js/spice-web-client/package.json
Normal file
16
js/spice-web-client/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "eyeos-spiceproxy",
|
||||
"version": "0.0.156",
|
||||
"description": "spice client network stack library",
|
||||
"main": "src/spiceproxy.js",
|
||||
"author": "eyeOS",
|
||||
"homepage": "http://www.eyeos.com",
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"eyeos-gruntfile": "*",
|
||||
"grunt": "0.4.5",
|
||||
"grunt-init": "0.3.2",
|
||||
"mocha": "2.4.5"
|
||||
}
|
||||
}
|
247
js/spice-web-client/process/busprocess.js
Normal file
247
js/spice-web-client/process/busprocess.js
Normal file
@ -0,0 +1,247 @@
|
||||
wdi.BUS_TYPES = {
|
||||
file: 0, // obsolete
|
||||
print: 1, // obsolete
|
||||
launchApplication: 2,
|
||||
windowManagement: 3,
|
||||
menu: 5,
|
||||
networkDriveManagement: 6,
|
||||
|
||||
|
||||
// Messages used during developing (for benchmarks and whatever).
|
||||
// you should not use them in code for production purposes.
|
||||
killApplicationDoNotUseInProductionEver: 34423423
|
||||
};
|
||||
|
||||
wdi.BusProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
busConnection: null,
|
||||
clientGui: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.clientGui = c.clientGui;
|
||||
this.busConnection = c.busConnection;
|
||||
},
|
||||
|
||||
process: function(message) {
|
||||
switch(message['verb']) {
|
||||
case "CONNECTED":
|
||||
this.busConnection.setSubscriptions();
|
||||
this.fire('busConnected');
|
||||
break;
|
||||
case "MESSAGE":
|
||||
this.parseMessage(message['body']);
|
||||
break;
|
||||
case "ERROR":
|
||||
console.error("Bus error");
|
||||
break;
|
||||
default:
|
||||
wdi.Debug.warn("Not implemented Stomp Verb: " + message['verb']);
|
||||
}
|
||||
},
|
||||
|
||||
parseMessage: function(body) {
|
||||
switch(parseInt(body['type'])) {
|
||||
case wdi.BUS_TYPES.launchApplication:
|
||||
this.parseLaunchApplicationMessage(body);
|
||||
break;
|
||||
case wdi.BUS_TYPES.killApplicationDoNotUseInProductionEver:
|
||||
// this is a message we send to the other side of the bus
|
||||
// so do nothing.
|
||||
break;
|
||||
case wdi.BUS_TYPES.windowManagement:
|
||||
this.parseWindowManagementMessage(body);
|
||||
break;
|
||||
case wdi.BUS_TYPES.menu:
|
||||
this.handleMenuMessage(body);
|
||||
break;
|
||||
case wdi.BUS_TYPES.networkDriveManagement:
|
||||
this._handleNetworkDriveMessage(body);
|
||||
break;
|
||||
default:
|
||||
wdi.Debug.info("Bus type '" + body['type'] + "' not implemented.");
|
||||
}
|
||||
},
|
||||
|
||||
_handleNetworkDriveMessage : function(message) {
|
||||
if(message.event != 'reMountNetworkDrive') {
|
||||
this.fire('networkDriveResponse', message);
|
||||
}
|
||||
},
|
||||
|
||||
getMenu: function() {
|
||||
this.busConnection.send(
|
||||
{
|
||||
"type": wdi.BUS_TYPES.menu,
|
||||
"value": false,
|
||||
"event": 'request'
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
handleMenuMessage: function(message) {
|
||||
if(message.event == 'response') {
|
||||
this.fire('menuResponse', message);
|
||||
}
|
||||
},
|
||||
|
||||
parseWindowManagementMessage: function(message) {
|
||||
switch (message['event']) {
|
||||
case 'windowList':
|
||||
case 'windowCreated':
|
||||
case 'windowClosed':
|
||||
case 'windowMoved':
|
||||
case 'windowResized':
|
||||
case 'windowFocused':
|
||||
case 'windowMinimized':
|
||||
case 'windowRestored':
|
||||
case 'windowMaximized':
|
||||
this.fire(message['event'], message['value']);
|
||||
break;
|
||||
default:
|
||||
wdi.Debug.info("Event '" + message['event'] + "' not implemented.")
|
||||
}
|
||||
},
|
||||
|
||||
closeWindow: function(hwnd) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'closeWindow',
|
||||
"hwnd": hwnd
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
moveWindow: function(hwnd, x, y) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'moveWindow',
|
||||
"hwnd": hwnd,
|
||||
"left": x,
|
||||
"top": y
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
minimizeWindow: function(hwnd) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'minimizeWindow',
|
||||
"hwnd": hwnd
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
maximizeWindow: function(hwnd) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'maximizeWindow',
|
||||
"hwnd": hwnd
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
restoreWindow: function(hwnd) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'restoreWindow',
|
||||
"hwnd": hwnd
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
focusWindow: function(hwnd) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'focusWindow',
|
||||
"hwnd": hwnd
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
resizeWindow: function(hwnd, width, height) {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'resizeWindow',
|
||||
"hwnd": hwnd,
|
||||
"width": width,
|
||||
"height": height
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
requestWindowList: function() {
|
||||
this.busConnection.send(
|
||||
this._constructWindowManagementMessage(
|
||||
{
|
||||
"event": 'getWindowList'
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
executeCommand: function(cmd) {
|
||||
this.busConnection.send(
|
||||
{
|
||||
"type": wdi.BUS_TYPES.launchApplication,
|
||||
"application": cmd
|
||||
}
|
||||
)
|
||||
|
||||
},
|
||||
|
||||
_constructWindowManagementMessage: function(obj) {
|
||||
if (obj['event'] === undefined) {
|
||||
throw new Error("You should pass an 'event' attribute in the object");
|
||||
}
|
||||
var ret = {
|
||||
'type': wdi.BUS_TYPES.windowManagement,
|
||||
'event': obj['event'],
|
||||
'value': {}
|
||||
};
|
||||
for (var i in obj) {
|
||||
if (i != 'event' && obj.hasOwnProperty(i)) {
|
||||
ret['value'][i] = obj[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
reMountNetworkDrive: function(host, username, password) {
|
||||
this.busConnection.send(
|
||||
{
|
||||
"type": wdi.BUS_TYPES.networkDriveManagement,
|
||||
"event": "reMountNetworkDrive",
|
||||
"host": host,
|
||||
"username": username,
|
||||
"password": password
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
parseLaunchApplicationMessage: function (message) {
|
||||
switch (message['event']) {
|
||||
case 'applicationLauncherWrongAppPathError':
|
||||
this.fire('wrongPathError', message);
|
||||
break;
|
||||
case 'applicationLaunchedSuccessfully':
|
||||
this.fire('applicationLaunchedSuccessfully', message);
|
||||
break;
|
||||
default:
|
||||
wdi.Debug.info("Event '" + message['event'] + "' not implemented.")
|
||||
}
|
||||
}
|
||||
});
|
83
js/spice-web-client/process/cursorprocess.js
Normal file
83
js/spice-web-client/process/cursorprocess.js
Normal file
@ -0,0 +1,83 @@
|
||||
wdi.CursorProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
imageData: null,
|
||||
|
||||
process: function(spiceMessage) {
|
||||
switch (spiceMessage.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_CURSOR_INIT:
|
||||
case wdi.SpiceVars.SPICE_MSG_CURSOR_SET:
|
||||
var wdiCursor = this.extractCursor(spiceMessage);
|
||||
if(wdiCursor) {
|
||||
wdi.VirtualMouse.setHotspot(0, 0);
|
||||
wdi.VirtualMouse.setMouse(wdiCursor.data, wdiCursor.header.hot_spot_x, wdiCursor.header.hot_spot_y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_toUrl:function(data) {
|
||||
var imageData = $('<canvas/>').attr({
|
||||
'width': data.width,
|
||||
'height': data.height
|
||||
})[0];
|
||||
var ctx = imageData.getContext('2d');
|
||||
ctx.putImageData(data, 0, 0);
|
||||
return imageData.toDataURL("image/png");
|
||||
},
|
||||
|
||||
extractCursor: function(spiceMessage) {
|
||||
var flags = spiceMessage.args.cursor.flags;
|
||||
var position = spiceMessage.args.position;
|
||||
var visible = spiceMessage.args.visible;
|
||||
|
||||
//if there is no cursor, return null
|
||||
if(flags & 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var imageData = null;
|
||||
|
||||
//cursor from cache?
|
||||
if(flags & wdi.SpiceCursorFlags.SPICE_CURSOR_FLAGS_FROM_CACHE) {
|
||||
imageData = wdi.ImageCache.getCursorFrom(spiceMessage.args.cursor);
|
||||
} else {
|
||||
//cursor from packet
|
||||
//any case should return url
|
||||
switch (spiceMessage.args.cursor.header.type) {
|
||||
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_ALPHA:
|
||||
imageData = this._toUrl(wdi.graphics.argbToImageData(spiceMessage.args.cursor.data, spiceMessage.args.cursor.header.width, spiceMessage.args.cursor.header.height));
|
||||
break;
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_MONO:
|
||||
imageData = this._toUrl(wdi.graphics.monoToImageData(spiceMessage.args.cursor.data, spiceMessage.args.cursor.header.width, spiceMessage.args.cursor.header.height));
|
||||
break;
|
||||
case 8:
|
||||
imageData = wdi.SpiceObject.bytesToString(spiceMessage.args.cursor.data);
|
||||
break;
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR4:
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR8:
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR16:
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR24:
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_COLOR32:
|
||||
case wdi.SpiceCursorType.SPICE_CURSOR_TYPE_ENUM_END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//got no cursor? error!
|
||||
if(!imageData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//we have cursor, cache it?
|
||||
if(flags & wdi.SpiceCursorFlags.SPICE_CURSOR_FLAGS_CACHE_ME) {
|
||||
wdi.ImageCache.addCursor(spiceMessage.args.cursor, imageData);
|
||||
}
|
||||
|
||||
return {
|
||||
data: imageData,
|
||||
position: position,
|
||||
visible: visible,
|
||||
header: spiceMessage.args.cursor.header
|
||||
};
|
||||
}
|
||||
});
|
119
js/spice-web-client/process/displaypreprocess.js
Normal file
119
js/spice-web-client/process/displaypreprocess.js
Normal file
@ -0,0 +1,119 @@
|
||||
wdi.DisplayPreProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
displayProcess: null,
|
||||
queued: [],
|
||||
inProcess: [],
|
||||
idleConsumers : [],
|
||||
consumers: [],
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.displayProcess = c.displayProcess || new wdi.DisplayProcess({
|
||||
clientGui: c.clientGui
|
||||
});
|
||||
this.clientGui = c.clientGui;
|
||||
|
||||
/**
|
||||
|
||||
Since javascript do not provide an API to check
|
||||
the number of cpu cores available, the best case for average computers
|
||||
and devices is 4.
|
||||
|
||||
If the computer doesn't have 4 or more available cores, there is only a little
|
||||
memory waste creating the threads and a bit of cpu overheat doing context
|
||||
switching.
|
||||
|
||||
There is an ongoing draft in w3c to standarize a way to detect this:
|
||||
|
||||
http://www.w3.org/2012/sysapps/device-capabilities/#cpu
|
||||
|
||||
**/
|
||||
if(c.numConsumers == null || c.numConsumers == undefined) c.numConsumers = 4;
|
||||
var numConsumers = c.numConsumers;
|
||||
|
||||
for(var i = 0;i<numConsumers; i++) {
|
||||
var consumer = new wdi.AsyncConsumer();
|
||||
this.consumers.push(consumer);
|
||||
this.idleConsumers.push(consumer);
|
||||
consumer.addListener('done', this.onConsumerDone, this);
|
||||
}
|
||||
},
|
||||
|
||||
onConsumerDone: function(e) {
|
||||
//we don't care about who has finished, only about the
|
||||
//state of the last item in queue
|
||||
var waitingTask = this.inProcess[0];
|
||||
var task = null;
|
||||
var i = 0;
|
||||
|
||||
while(waitingTask && waitingTask.state === 1) {
|
||||
task = this.inProcess.shift();
|
||||
try {
|
||||
this.displayProcess.process(task.message);
|
||||
} catch(e) {
|
||||
wdi.Debug.error("DisplayPreProcess error: ", e);
|
||||
}
|
||||
waitingTask = this.inProcess[0];
|
||||
i++;
|
||||
}
|
||||
|
||||
//put the consumer as idle
|
||||
this.idleConsumers.push(e);
|
||||
//continue processing!
|
||||
if(this.queued.length > 0) {
|
||||
this.executeConsumer();
|
||||
}
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
this.addTask(spiceMessage); //first of all, queue it
|
||||
//it is the only item in the list?
|
||||
//we are the only message in the queue... process?
|
||||
this.executeConsumer();
|
||||
},
|
||||
|
||||
addTask: function(spiceMessage) {
|
||||
this.queued.push({
|
||||
message: spiceMessage,
|
||||
clientGui: this.clientGui
|
||||
});
|
||||
},
|
||||
|
||||
getNextTask : function () {
|
||||
var task = this.queued.shift();
|
||||
while(typeof task == 'undefined' && this.queued.length != 0) {
|
||||
task = this.queued.shift();
|
||||
}
|
||||
|
||||
//we found a task?
|
||||
if(typeof task == 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
task.state = 0;
|
||||
this.inProcess.push(task); //add the task to the inProcess list
|
||||
return task;
|
||||
},
|
||||
|
||||
executeConsumer: function() {
|
||||
//check if there are idle consumers
|
||||
if(this.idleConsumers.length > 0) {
|
||||
wdi.Debug.log('DisplayPreProcess: available workers: '+this.idleConsumers.length);
|
||||
wdi.Debug.log('DisplaypreProcess: pending tasks: '+this.queued.length);
|
||||
//idle consumer found
|
||||
var consumer = this.idleConsumers.shift();
|
||||
//execute the next task in this consumer
|
||||
var task = this.getNextTask();
|
||||
|
||||
if(task) {
|
||||
consumer.consume(task);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
this.consumers.forEach(function (consumer) {
|
||||
consumer.dispose();
|
||||
});
|
||||
}
|
||||
});
|
174
js/spice-web-client/process/displayprocess.js
Normal file
174
js/spice-web-client/process/displayprocess.js
Normal file
@ -0,0 +1,174 @@
|
||||
wdi.DisplayProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
runQ: null,
|
||||
packetFilter: null,
|
||||
|
||||
init: function(c) {
|
||||
this.runQ = c.runQ || wdi.ExecutionControl.runQ;
|
||||
this.packetFilter = c.packetFilter || wdi.PacketFilter;
|
||||
this.clientGui = c.clientGui;
|
||||
this.displayRouter = c.displayRouter || new wdi.DisplayRouter({clientGui:this.clientGui});
|
||||
this.started = false;
|
||||
this.waitingMessages = [];
|
||||
this.packetWorkerIdentifier = c.packetWorkerIdentifier || new wdi.PacketWorkerIdentifier();
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
//this._process(spiceMessage);
|
||||
//disable requestanimationframe equivalent for the moment
|
||||
//the remove redundant draws implementation is buggy
|
||||
//and there are considerations on how it is implemented
|
||||
|
||||
|
||||
var self = this;
|
||||
this.waitingMessages.push(spiceMessage);
|
||||
|
||||
if(!this.started) {
|
||||
this.timer = setInterval(function() {
|
||||
self.flush();
|
||||
}, 50);
|
||||
this.started = true;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
if(this.waitingMessages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var spiceMessage;
|
||||
|
||||
//remove redundant draws
|
||||
this.removeRedundantDraws();
|
||||
|
||||
var size = this.waitingMessages.length;
|
||||
|
||||
while(i < size) {
|
||||
spiceMessage = this.waitingMessages[i];
|
||||
this._process(spiceMessage);
|
||||
i++;
|
||||
}
|
||||
|
||||
this.waitingMessages = [];
|
||||
},
|
||||
|
||||
removeRedundantDraws: function() {
|
||||
if(this.waitingMessages.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
var size = this.waitingMessages.length;
|
||||
var message, body, imageProperties, rop, base;
|
||||
var collision_boxes = {};
|
||||
var to_delete = [];
|
||||
var deleted = false;
|
||||
var surface_id;
|
||||
var packetBox;
|
||||
var box;
|
||||
var i;
|
||||
var x;
|
||||
while(size--) {
|
||||
message = this.waitingMessages[size];
|
||||
//should remove any packet from the past overwritten by this one
|
||||
body = message.args;
|
||||
base = body.base;
|
||||
|
||||
rop = body.rop_descriptor;
|
||||
deleted = false;
|
||||
|
||||
//TODO TODO TODO: there is need for a special case for draw_copy_bits?!
|
||||
//we need base to have a box
|
||||
if(base) {
|
||||
surface_id = base.surface_id;
|
||||
packetBox = base.box;
|
||||
surface_id = base.surface_id;
|
||||
//check if this packet is occluded by another packet
|
||||
imageProperties = this.packetWorkerIdentifier.getImageProperties(message);
|
||||
//if there is no image properties, or there is but cache flags are 0
|
||||
if(!collision_boxes[surface_id]) {
|
||||
collision_boxes[surface_id] = [];
|
||||
}
|
||||
|
||||
if((!imageProperties || (imageProperties && !(imageProperties.descriptor.flags & wdi.SpiceImageFlags.SPICE_IMAGE_FLAGS_CACHE_ME))) && surface_id === 0) {
|
||||
for(i=0; i<collision_boxes[surface_id].length; i++) {
|
||||
//check if base.box is inside one of the rectangles in collision_boxes
|
||||
box = collision_boxes[surface_id][i];
|
||||
if(box.bottom >= packetBox.bottom && box.top <= packetBox.top && box.left <= packetBox.left
|
||||
&& box.right >= packetBox.right ) {
|
||||
|
||||
deleted = true;
|
||||
to_delete.push(size);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if the message is still alive, and if it is, then put its box into collision_boxes if the message
|
||||
//will overWrite its screen area when painted
|
||||
//atm only drawcopy and drawfill have overwritescreenarea set
|
||||
if(!deleted && message.messageType === wdi.SpiceVars.SPICE_MSG_DISPLAY_COPY_BITS) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!deleted && body.getMessageProperty('overWriteScreenArea', false) && base.clip.type == 0 && rop == wdi.SpiceRopd.SPICE_ROPD_OP_PUT) {
|
||||
collision_boxes[surface_id].push(base.box);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//itareate over messages marked for deletion and remove it from the array
|
||||
for(x = 0;x < to_delete.length;x++) {
|
||||
this.waitingMessages.splice(to_delete[x], 1);
|
||||
}
|
||||
},
|
||||
|
||||
_process: function(spiceMessage) {
|
||||
if (wdi.logOperations) {
|
||||
wdi.DataLogger.log(spiceMessage, 0, null, true, '', '_decode');
|
||||
}
|
||||
//append the message to the runqueue
|
||||
//so the packet is not executed until the previous packets
|
||||
//finished processing
|
||||
this.runQ.add(function(proxy) {
|
||||
|
||||
//pass the message through the packet filter
|
||||
//so the packet can be filtered, logged, etc
|
||||
this.packetFilter.filter(spiceMessage, function(message) {
|
||||
wdi.ExecutionControl.currentProxy = proxy;
|
||||
//process the packet
|
||||
this.displayRouter.processPacket(message);
|
||||
//post process operations
|
||||
this.postProcess();
|
||||
}, this, this.clientGui);
|
||||
|
||||
|
||||
//if the packet was synchronous, process next packet
|
||||
if (wdi.ExecutionControl.sync) {
|
||||
proxy.end();
|
||||
}
|
||||
//Now message could be asynchronous
|
||||
}, this, function() {
|
||||
//this is executed when the message has finished processing
|
||||
//we use processEnd to notify packetFilter about the ending of processing
|
||||
//the current message
|
||||
this.processEnd(spiceMessage, this.clientGui);
|
||||
|
||||
});
|
||||
|
||||
//if this is the first message in the queue, execute it
|
||||
//if not, this call will have no effect.
|
||||
this.runQ.process();
|
||||
|
||||
},
|
||||
|
||||
processEnd: function(spiceMessage, clientGui) {
|
||||
this.packetFilter.notifyEnd(spiceMessage, clientGui);
|
||||
},
|
||||
|
||||
postProcess: function() {
|
||||
//TEST METHOD DON'T DELETE
|
||||
}
|
||||
});
|
||||
|
86
js/spice-web-client/process/inputprocess.js
Normal file
86
js/spice-web-client/process/inputprocess.js
Normal file
@ -0,0 +1,86 @@
|
||||
wdi.InputProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
clientGui: null,
|
||||
spiceConnection: null,
|
||||
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.clientGui = c.clientGui;
|
||||
this.spiceConnection = c.spiceConnection;
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
switch (spiceMessage.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
|
||||
this.clientGui.motion_ack();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
send: function(data, type) {
|
||||
var packet, scanCodes, i;
|
||||
if(type == 'mousemove') {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_POSITION,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.RedcMousePosition({
|
||||
x:data[1][0]+wdi.VirtualMouse.hotspot.x,
|
||||
y:data[1][1]+wdi.VirtualMouse.hotspot.y,
|
||||
buttons_state:data[1][2],
|
||||
display_id:0
|
||||
})
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
} else if(type == 'mousedown') {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_PRESS,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.RedcMousePress({
|
||||
button_id:data[1]+1,
|
||||
buttons_state:1<<data[1]
|
||||
})
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
} else if(type == 'mouseup') {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_RELEASE,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.RedcMousePress({
|
||||
button_id:data[1]+1,
|
||||
buttons_state:0
|
||||
})
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
} else if (type == 'keydown' || type == 'keypress') {
|
||||
scanCodes = wdi.Keymap.getScanCodes(data[1][0]);
|
||||
for (i= 0; i<scanCodes.length;i++) {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_KEY_DOWN,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.SpiceScanCode(scanCodes[i])
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
}
|
||||
} else if (type == 'keyup') {
|
||||
scanCodes = wdi.Keymap.getScanCodes(data[1][0]);
|
||||
for (i= 0; i<scanCodes.length;i++) {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_KEY_UP,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.SpiceScanCode(scanCodes[i])
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
}
|
||||
} else if(type == 'joystick') {
|
||||
packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_INPUTS_MOUSE_MOTION,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_INPUTS,
|
||||
args: new wdi.RedcMouseMotion({
|
||||
x:data[1][0],
|
||||
y:data[1][1],
|
||||
buttons_state:0
|
||||
})
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
}
|
||||
}
|
||||
});
|
52
js/spice-web-client/process/mainprocess.js
Normal file
52
js/spice-web-client/process/mainprocess.js
Normal file
@ -0,0 +1,52 @@
|
||||
wdi.MainProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
init: function(c) {
|
||||
this.superInit();
|
||||
this.app = c.app;
|
||||
this.spiceConnection = c.app.spiceConnection;
|
||||
this.agent = c.app.agent;
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
var channel = this.spiceConnection.channels[wdi.SpiceVars.SPICE_CHANNEL_MAIN];
|
||||
|
||||
switch(spiceMessage.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_INIT:
|
||||
channel.connectionId = spiceMessage.args.session_id;
|
||||
channel.fire('connectionId', channel.connectionId);
|
||||
if(spiceMessage.args.agent_connected == 1) {
|
||||
channel.fire('initAgent', spiceMessage.args.agent_tokens);
|
||||
}
|
||||
if (spiceMessage.args.current_mouse_mode == 1) {
|
||||
channel.fire('mouseMode', spiceMessage.args.current_mouse_mode);
|
||||
}
|
||||
// the mouse mode must be change both if we have agent or not
|
||||
this.changeMouseMode();
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_DATA:
|
||||
var packet = spiceMessage.args;
|
||||
this.agent.onAgentData(packet);
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_AGENT_CONNECTED:
|
||||
channel.fire('initAgent', spiceMessage.args.agent_tokens);
|
||||
this.changeMouseMode();
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
|
||||
this.app.multimediaTime = spiceMessage.args.multimedia_time;
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_MAIN_CHANNELS_LIST:
|
||||
channel.fire('channelListAvailable', spiceMessage.args.channels);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
changeMouseMode: function() {
|
||||
var packet = new wdi.SpiceMessage({
|
||||
messageType: wdi.SpiceVars.SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST,
|
||||
channel: wdi.SpiceVars.SPICE_CHANNEL_MAIN,
|
||||
args: new wdi.SpiceMouseModeRequest({
|
||||
request_mode: 2
|
||||
})
|
||||
});
|
||||
this.spiceConnection.send(packet);
|
||||
}
|
||||
});
|
201
js/spice-web-client/process/playbackprocess.js
Normal file
201
js/spice-web-client/process/playbackprocess.js
Normal file
@ -0,0 +1,201 @@
|
||||
wdi.PlaybackProcess = $.spcExtend(wdi.EventObject.prototype, {
|
||||
_lastApp: null,
|
||||
started: false,
|
||||
minBuffSize: 1024*32,
|
||||
frequency: null,
|
||||
channels: null,
|
||||
audioContext: null,
|
||||
startTime: null, // controls the playback time if no delay occurs
|
||||
hasAudioSupport: true, //whether the browser supports HTML5 Web Audio API
|
||||
|
||||
typedBuffer: null,
|
||||
position: null,
|
||||
|
||||
|
||||
init: function(c) {
|
||||
this.app = c.app;
|
||||
this.audioContext = this.getAudioContext();
|
||||
if (this.audioContext) {
|
||||
this.hasAudioSupport = true;
|
||||
} else {
|
||||
this.hasAudioSupport = false;
|
||||
wdi.Debug.warn('The client browser does not support Web Audio API');
|
||||
}
|
||||
this.startTime = 0;
|
||||
this.typedBuffer = new ArrayBuffer(1024*32);
|
||||
this.position = 0;
|
||||
},
|
||||
|
||||
getAudioContext: function() {
|
||||
//standard browser object
|
||||
try {
|
||||
return new AudioContext();
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
//chrome and safari
|
||||
try {
|
||||
return new webkitAudioContext();
|
||||
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
process: function(spiceMessage) {
|
||||
|
||||
// if (this.hasAudioSupport && !Modernizr.touch) {
|
||||
if (this.hasAudioSupport) {
|
||||
switch (spiceMessage.messageType) {
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_MODE:
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_START:
|
||||
var packet = spiceMessage.args;
|
||||
this.channels = packet.channels;
|
||||
this.frequency = packet.frequency;
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_STOP:
|
||||
this.startTime = 0;
|
||||
var packet = spiceMessage.args;
|
||||
this.flush();
|
||||
break;
|
||||
case wdi.SpiceVars.SPICE_MSG_PLAYBACK_DATA:
|
||||
// While we receive data chunks, we store them in a buffer, so than when it is full we play the sound and empty it.
|
||||
// With this we get a more fluid playback and better overall performance than if we just played the data the moment we got it
|
||||
var packet = spiceMessage.args;
|
||||
var dataTimestamp = spiceMessage.args.multimedia_time;
|
||||
|
||||
|
||||
var tmpview = new Uint8Array(this.typedBuffer);
|
||||
tmpview.set(packet.data, this.position);
|
||||
this.position += packet.data.length;
|
||||
this._lastApp = this.app;
|
||||
|
||||
if(this.position >= this.minBuffSize) {
|
||||
// ok, the buffer is full. We send the data to be played and later we can empty it to make room for more audio
|
||||
this.flush(dataTimestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
//TODO:
|
||||
// If the browser doesn't support Web Audio, we could still attach a wav header to the raw PCM we receive from spice and use the more widespread supported audio tag
|
||||
// Meanwhile, we can skip all the audio packets and gain some performance at least
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Plays all the audio buffer and empties it
|
||||
*
|
||||
* @param app
|
||||
* @param dataTimestamp
|
||||
*/
|
||||
flush: function(dataTimestamp) {
|
||||
if(this.position > 0) {
|
||||
if (this.started) {
|
||||
this.playSound(this.typedBuffer, dataTimestamp);
|
||||
}
|
||||
this.position = 0;
|
||||
this.typedBuffer = new ArrayBuffer(1024*32);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Plays the raw pcm data passed as param using HTML5's Web Audio API
|
||||
*
|
||||
* @param buffer
|
||||
*/
|
||||
playSound: function(buffer, dataTimestamp) {
|
||||
if(this.channels == 2) {
|
||||
return this.playSoundStereo(buffer, dataTimestamp);
|
||||
}
|
||||
|
||||
var audio = new Int16Array(buffer);
|
||||
|
||||
var channelData = new Array(this.channels);
|
||||
for(var i = 0;i<this.channels;i++) {
|
||||
channelData[i] = new Float32Array(audio.length / 2);
|
||||
}
|
||||
|
||||
var channelCounter = 0;
|
||||
for (var i = 0; i < audio.length; ) {
|
||||
for(var c = 0; c < this.channels; c++) {
|
||||
//because the audio data spice gives us is 16 bits signed int (32768) and we wont to get a float out of it (between -1.0' and 1.0)
|
||||
channelData[c][channelCounter] = audio[i++] / 32768;
|
||||
}
|
||||
channelCounter++;
|
||||
}
|
||||
|
||||
var source = this.audioContext['createBufferSource'](); // creates a sound source
|
||||
var audioBuffer = this.audioContext['createBuffer'](this.channels, channelCounter, this.frequency);
|
||||
for(var i=0;i < this.channels; i++) {
|
||||
audioBuffer['getChannelData'](i)['set'](channelData[i]);
|
||||
}
|
||||
|
||||
this._play(source, audioBuffer, dataTimestamp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Plays the raw pcm STEREO data passed as param using HTML5's Web Audio API
|
||||
*
|
||||
* @param buffer
|
||||
*/
|
||||
playSoundStereo: function(buffer, dataTimestamp) {
|
||||
// Each data packet is 16 bits, the first being left channel data and the second being right channel data (LR-LR-LR-LR...)
|
||||
var audio = new Int16Array(buffer);
|
||||
|
||||
// We split the audio buffer in two channels. Float32Array is the type required by Web Audio API
|
||||
var left = new Float32Array(audio.length / 2);
|
||||
var right = new Float32Array(audio.length / 2);
|
||||
|
||||
var channelCounter = 0;
|
||||
|
||||
var audioContext = this.audioContext;
|
||||
var len = audio.length;
|
||||
|
||||
for (var i = 0; i < len; ) {
|
||||
//because the audio data spice gives us is 16 bits signed int (32768) and we wont to get a float out of it (between -1.0 and 1.0)
|
||||
left[channelCounter] = audio[i++] / 32768;
|
||||
right[channelCounter] = audio[i++] / 32768;
|
||||
channelCounter++;
|
||||
}
|
||||
|
||||
var source = audioContext['createBufferSource'](); // creates a sound source
|
||||
var audioBuffer = audioContext['createBuffer'](2, channelCounter, this.frequency);
|
||||
|
||||
audioBuffer['getChannelData'](0)['set'](left);
|
||||
audioBuffer['getChannelData'](1)['set'](right);
|
||||
|
||||
this._play(source, audioBuffer, dataTimestamp);
|
||||
},
|
||||
|
||||
_play: function(source, audioBuffer, dataTimestamp) {
|
||||
var wait = 0;
|
||||
if (dataTimestamp) {
|
||||
var elapsedTime = Date.now() - this.app.lastMultimediaTime; // time passed since we received the last multimedia time from main channel
|
||||
var currentMultimediaTime = elapsedTime + this.app.multimediaTime; // total delay we have at the moment
|
||||
wait = dataTimestamp - currentMultimediaTime;
|
||||
if (wait < 0) {
|
||||
wait = 0;
|
||||
}
|
||||
}
|
||||
source['buffer'] = audioBuffer;
|
||||
source['connect'](this.audioContext['destination']); // connect the source to the context's destination (the speakers)
|
||||
|
||||
//if (!Modernizr.touch) {
|
||||
source['start'](this.startTime + wait); // play the source now
|
||||
//} else {
|
||||
// source.noteOn(0);
|
||||
//}
|
||||
|
||||
this.startTime += audioBuffer.duration;
|
||||
},
|
||||
|
||||
startAudio: function () {
|
||||
this.started = true;
|
||||
this.flush();
|
||||
}
|
||||
});
|
BIN
js/spice-web-client/resources/magnifier.png
Normal file
BIN
js/spice-web-client/resources/magnifier.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
js/spice-web-client/resources/mouse.png
Normal file
BIN
js/spice-web-client/resources/mouse.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 381 B |
BIN
js/spice-web-client/resources/mouse_cursor.gif
Normal file
BIN
js/spice-web-client/resources/mouse_cursor.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 959 B |
BIN
js/spice-web-client/resources/mouse_cursor.png
Normal file
BIN
js/spice-web-client/resources/mouse_cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
177
js/spice-web-client/run.js
Normal file
177
js/spice-web-client/run.js
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
function getURLParameter (name) {
|
||||
return decodeURIComponent(
|
||||
(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)')
|
||||
.exec(location.search) || [, ""])[1]
|
||||
.replace(/\+/g, '%20')
|
||||
) || null;
|
||||
}
|
||||
|
||||
|
||||
wdi.Debug.debug = false; //enable logging to javascript console
|
||||
wdi.exceptionHandling = false; //disable "global try catch" to improve debugging
|
||||
//if enabled, console errors do not include line numbers
|
||||
//wdi.SeamlessIntegration = false; //enable window integration. (if disabled, full desktop is received)
|
||||
|
||||
wdi.IntegrationBenchmarkEnabled = false;// MS Excel loading time benchmark
|
||||
|
||||
function start () {
|
||||
app = new Application();
|
||||
|
||||
var f = function (action, params) {
|
||||
if (action == 'windowClosed') {
|
||||
$(params.canvas).remove();
|
||||
$(params.eventLayer).remove();
|
||||
} else if (action == 'windowMoved') {
|
||||
$(params.canvas).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
$(params.eventLayer).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
} else if (action == 'init' || action == 'windowCreated') {
|
||||
var item = null;
|
||||
var canvas = null;
|
||||
var eventlayer = null;
|
||||
var body = $('body');
|
||||
|
||||
for (var i in params) {
|
||||
item = params[i];
|
||||
var position = item.position * 2;
|
||||
canvas = $(item.canvas).css({
|
||||
'zIndex': 10000 - position - 1,
|
||||
'position': 'absolute',
|
||||
'top': item.info.top + 'px',
|
||||
'left': item.info.left + 'px'
|
||||
});
|
||||
eventlayer = $(item.eventLayer).css({
|
||||
'top': item.info.top + 'px',
|
||||
'left': item.info.left + 'px',
|
||||
'zIndex': 10000 - position
|
||||
})
|
||||
body.append(canvas);
|
||||
body.append(eventlayer);
|
||||
}
|
||||
} else if (action == 'ready') {
|
||||
var width = $(window).width();
|
||||
var height = $(window).height();
|
||||
|
||||
app.sendCommand('setResolution', {
|
||||
'width': width,
|
||||
'height': height
|
||||
});
|
||||
} else if (action == 'resolution') {
|
||||
|
||||
} else if (action == 'windowMinimized') {
|
||||
//in eyeos, this should minimize the window, not close it
|
||||
$(params.canvas).css({'display': 'none'});
|
||||
$(params.eventLayer).css({'display': 'none'});
|
||||
} else if (action == 'windowMaximized') {
|
||||
$(params.canvas).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
$(params.eventLayer).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
} else if (action == 'windowRestored') {
|
||||
//in eyeos, this should restore the window
|
||||
$(params.canvas).css({'display': 'block'});
|
||||
$(params.eventLayer).css({'display': 'block'});
|
||||
$(params.canvas).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
$(params.eventLayer).css({
|
||||
'top': params.info.top + 'px',
|
||||
'left': params.info.left + 'px'
|
||||
});
|
||||
} else if (action == 'windowFocused') {
|
||||
//debugger; //eyeos should move the window to front!
|
||||
} else if (action == 'timeLapseDetected') {
|
||||
wdi.Debug.log('Detected time lapse of ', params, 'seconds');
|
||||
} else if (action == 'error') {
|
||||
// alert('error');
|
||||
} else if ("checkResults") {
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
$(window)['resize'](function () {
|
||||
app.sendCommand('setResolution', {
|
||||
'width': $(window).width(),
|
||||
'height': $(window).height()
|
||||
});
|
||||
});
|
||||
|
||||
var useWorkers = true;
|
||||
|
||||
app.run({
|
||||
'callback': f,
|
||||
'context': this,
|
||||
'host': getURLParameter('host') || '127.0.0.1',
|
||||
'port': getURLParameter('port') || 8080,
|
||||
'protocol': getURLParameter('protocol') || 'ws',
|
||||
'token': '1q2w3e4r',
|
||||
'vmHost': getURLParameter('vmhost') || false,
|
||||
'vmPort': getURLParameter('vmport') || false,
|
||||
'useBus': false,
|
||||
'busHost': '10.11.12.200',
|
||||
'busPort': 61613,
|
||||
'busSubscriptions': ['/topic/00000000-0000-0000-0000-000000000000'],
|
||||
'busUser': '00000000-0000-0000-0000-000000000000',
|
||||
'busPass': 'potato',
|
||||
// Connection Control
|
||||
'connectionControl': false,
|
||||
'heartbeatToken': 'heartbeat',
|
||||
'heartbeatTimeout': 4000,//miliseconds
|
||||
'busFileServerBaseUrl': 'https://10.11.12.200/fileserver/',
|
||||
'layout': 'es',
|
||||
'clientOffset': {
|
||||
'x': 0,
|
||||
'y': 0
|
||||
},
|
||||
'useWorkers': useWorkers,
|
||||
'seamlessDesktopIntegration': false,
|
||||
'externalClipboardHandling': false,
|
||||
'disableClipboard': true,
|
||||
'layer': document.getElementById('testVdi'),
|
||||
'vmInfoToken': getURLParameter('vmInfoToken')
|
||||
//'language': navigator.language
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(start);
|
13
js/spice-web-client/sonar.properties
Normal file
13
js/spice-web-client/sonar.properties
Normal file
@ -0,0 +1,13 @@
|
||||
sonar.projectKey=spiceproxy
|
||||
sonar.projectName=spiceproxy
|
||||
sonar.projectVersion=1.0.0
|
||||
sonar.sources=src
|
||||
sonar.language=js
|
||||
sonar.dynamicAnalysis=reuseReports
|
||||
sonar.javascript.jstest.reportsPath=build/reports
|
||||
sonar.javascript.lcov.reportPath=build/reports/lcov.info
|
||||
sonar.exclusions=**/node_modules/**/*, \
|
||||
**/test/**/*, \
|
||||
**/component-test/**/*, \
|
||||
**/build/**/*, \
|
||||
**/doc/**/*
|
517
js/spice-web-client/spiceobjects/generated/protocol.js
Normal file
517
js/spice-web-client/spiceobjects/generated/protocol.js
Normal file
@ -0,0 +1,517 @@
|
||||
/*
|
||||
eyeOS Spice Web Client
|
||||
Copyright (c) 2015 eyeOS S.L.
|
||||
|
||||
Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License version 3 as published by the
|
||||
Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
version 3 along with this program in the file "LICENSE". If not, see
|
||||
<http://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||
|
||||
See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org
|
||||
|
||||
The interactive user interfaces in modified source and object code versions
|
||||
of this program must display Appropriate Legal Notices, as required under
|
||||
Section 5 of the GNU Affero General Public License version 3.
|
||||
|
||||
In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
these Appropriate Legal Notices must retain the display of the "Powered by
|
||||
eyeos" logo and retain the original copyright notice. If the display of the
|
||||
logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
|
||||
must display the words "Powered by eyeos" and retain the original copyright notice.
|
||||
*/
|
||||
|
||||
wdi.SpicePubkeyType = {
|
||||
SPICE_PUBKEY_TYPE_INVALID:0,
|
||||
SPICE_PUBKEY_TYPE_RSA:1,
|
||||
SPICE_PUBKEY_TYPE_RSA2:2,
|
||||
SPICE_PUBKEY_TYPE_DSA:3,
|
||||
SPICE_PUBKEY_TYPE_DSA1:4,
|
||||
SPICE_PUBKEY_TYPE_DSA2:5,
|
||||
SPICE_PUBKEY_TYPE_DSA3:6,
|
||||
SPICE_PUBKEY_TYPE_DSA4:7,
|
||||
SPICE_PUBKEY_TYPE_DH:8,
|
||||
SPICE_PUBKEY_TYPE_EC:9,
|
||||
SPICE_PUBKEY_TYPE_ENUM_END:10
|
||||
}
|
||||
|
||||
wdi.SpiceWarnCode = {
|
||||
SPICE_WARN_GENERAL:0,
|
||||
SPICE_WARN_CODE_ENUM_END:1
|
||||
}
|
||||
|
||||
wdi.SpiceLineFlags = {
|
||||
SPICE_LINE_FLAGS_START_WITH_GAP:4,
|
||||
SPICE_LINE_FLAGS_STYLED:8,
|
||||
SPICE_LINE_FLAGS_MASK:12
|
||||
}
|
||||
|
||||
wdi.SpiceNotifyVisibility = {
|
||||
SPICE_NOTIFY_VISIBILITY_LOW:0,
|
||||
SPICE_NOTIFY_VISIBILITY_MEDIUM:1,
|
||||
SPICE_NOTIFY_VISIBILITY_HIGH:2,
|
||||
SPICE_NOTIFY_VISIBILITY_ENUM_END:3
|
||||
}
|
||||
|
||||
wdi.SpiceVars = {
|
||||
SPICE_MSGC_ACK_SYNC:1,
|
||||
SPICE_MSGC_ACK:2,
|
||||
SPICE_MSGC_PONG:3,
|
||||
SPICE_MSGC_MIGRATE_FLUSH_MARK:4,
|
||||
SPICE_MSGC_MIGRATE_DATA:5,
|
||||
SPICE_MSGC_DISCONNECTING:6,
|
||||
SPICE_MSGC_DISPLAY_INIT:101,
|
||||
SPICE_MSGC_END_DISPLAY:102,
|
||||
SPICE_CHANNEL_MAIN:1,
|
||||
SPICE_CHANNEL_DISPLAY:2,
|
||||
SPICE_CHANNEL_INPUTS:3,
|
||||
SPICE_CHANNEL_CURSOR:4,
|
||||
SPICE_CHANNEL_PLAYBACK:5,
|
||||
SPICE_CHANNEL_RECORD:6,
|
||||
SPICE_CHANNEL_TUNNEL:7,
|
||||
SPICE_CHANNEL_SMARTCARD:8,
|
||||
SPICE_CHANNEL_USBREDIR:9,
|
||||
SPICE_END_CHANNEL:10,
|
||||
SPICE_MSG_MIGRATE:1,
|
||||
SPICE_MSG_MIGRATE_DATA:2,
|
||||
SPICE_MSG_SET_ACK:3,
|
||||
SPICE_MSG_PING:4,
|
||||
SPICE_MSG_WAIT_FOR_CHANNELS:5,
|
||||
SPICE_MSG_DISCONNECTING:6,
|
||||
SPICE_MSG_NOTIFY:7,
|
||||
SPICE_MSG_LIST:8,
|
||||
SPICE_MSGC_MAIN_CLIENT_INFO:101,
|
||||
SPICE_MSGC_MAIN_MIGRATE_CONNECTED:102,
|
||||
SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:103,
|
||||
SPICE_MSGC_MAIN_ATTACH_CHANNELS:104,
|
||||
SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:105,
|
||||
SPICE_MSGC_MAIN_AGENT_START:106,
|
||||
SPICE_MSGC_MAIN_AGENT_DATA:107,
|
||||
SPICE_MSGC_MAIN_AGENT_TOKEN:108,
|
||||
SPICE_MSGC_MAIN_MIGRATE_END:109,
|
||||
SPICE_MSGC_END_MAIN:110,
|
||||
SPICE_MSG_DISPLAY_MODE:101,
|
||||
SPICE_MSG_DISPLAY_MARK:102,
|
||||
SPICE_MSG_DISPLAY_RESET:103,
|
||||
SPICE_MSG_DISPLAY_COPY_BITS:104,
|
||||
SPICE_MSG_DISPLAY_INVAL_LIST:105,
|
||||
SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS:106,
|
||||
SPICE_MSG_DISPLAY_INVAL_PALETTE:107,
|
||||
SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES:108,
|
||||
SPICE_MSG_DISPLAY_STREAM_CREATE:122,
|
||||
SPICE_MSG_DISPLAY_STREAM_DATA:123,
|
||||
SPICE_MSG_DISPLAY_STREAM_CLIP:124,
|
||||
SPICE_MSG_DISPLAY_STREAM_DESTROY:125,
|
||||
SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL:126,
|
||||
SPICE_MSG_DISPLAY_DRAW_FILL:302,
|
||||
SPICE_MSG_DISPLAY_DRAW_OPAQUE:303,
|
||||
SPICE_MSG_DISPLAY_DRAW_COPY:304,
|
||||
SPICE_MSG_DISPLAY_DRAW_BLEND:305,
|
||||
SPICE_MSG_DISPLAY_DRAW_BLACKNESS:306,
|
||||
SPICE_MSG_DISPLAY_DRAW_WHITENESS:307,
|
||||
SPICE_MSG_DISPLAY_DRAW_INVERS:308,
|
||||
SPICE_MSG_DISPLAY_DRAW_ROP3:309,
|
||||
SPICE_MSG_DISPLAY_DRAW_STROKE:310,
|
||||
SPICE_MSG_DISPLAY_DRAW_TEXT:311,
|
||||
SPICE_MSG_DISPLAY_DRAW_TRANSPARENT:312,
|
||||
SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND:313,
|
||||
SPICE_MSG_DISPLAY_SURFACE_CREATE:314,
|
||||
SPICE_MSG_DISPLAY_SURFACE_DESTROY:315,
|
||||
SPICE_MSG_END_DISPLAY:316,
|
||||
SPICE_MSG_INPUTS_INIT:101,
|
||||
SPICE_MSG_INPUTS_KEY_MODIFIERS:102,
|
||||
SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:111,
|
||||
SPICE_MSG_END_INPUTS:112,
|
||||
SPICE_MSGC_INPUTS_KEY_DOWN:101,
|
||||
SPICE_MSGC_INPUTS_KEY_UP:102,
|
||||
SPICE_MSGC_INPUTS_KEY_MODIFIERS:103,
|
||||
SPICE_MSGC_INPUTS_MOUSE_MOTION:111,
|
||||
SPICE_MSGC_INPUTS_MOUSE_POSITION:112,
|
||||
SPICE_MSGC_INPUTS_MOUSE_PRESS:113,
|
||||
SPICE_MSGC_INPUTS_MOUSE_RELEASE:114,
|
||||
SPICE_MSGC_END_INPUTS:115,
|
||||
SPICE_MSG_CURSOR_INIT:101,
|
||||
SPICE_MSG_CURSOR_RESET:102,
|
||||
SPICE_MSG_CURSOR_SET:103,
|
||||
SPICE_MSG_CURSOR_MOVE:104,
|
||||
SPICE_MSG_CURSOR_HIDE:105,
|
||||
SPICE_MSG_CURSOR_TRAIL:106,
|
||||
SPICE_MSG_CURSOR_INVAL_ONE:107,
|
||||
SPICE_MSG_CURSOR_INVAL_ALL:108,
|
||||
SPICE_MSG_END_CURSOR:109,
|
||||
SPICE_MSG_RECORD_START:101,
|
||||
SPICE_MSG_RECORD_STOP:102,
|
||||
SPICE_MSG_RECORD_VOLUME:103,
|
||||
SPICE_MSG_RECORD_MUTE:104,
|
||||
SPICE_MSG_END_RECORD:105,
|
||||
SPICE_MSGC_SMARTCARD_DATA:101,
|
||||
SPICE_MSGC_END_SMARTCARD:102,
|
||||
SPICE_MSGC_SPICEVMC_DATA:101,
|
||||
SPICE_MSGC_END_SPICEVMC:102,
|
||||
SPICE_MSG_MAIN_MIGRATE_BEGIN:101,
|
||||
SPICE_MSG_MAIN_MIGRATE_CANCEL:102,
|
||||
SPICE_MSG_MAIN_INIT:103,
|
||||
SPICE_MSG_MAIN_CHANNELS_LIST:104,
|
||||
SPICE_MSG_MAIN_MOUSE_MODE:105,
|
||||
SPICE_MSG_MAIN_MULTI_MEDIA_TIME:106,
|
||||
SPICE_MSG_MAIN_AGENT_CONNECTED:107,
|
||||
SPICE_MSG_MAIN_AGENT_DISCONNECTED:108,
|
||||
SPICE_MSG_MAIN_AGENT_DATA:109,
|
||||
SPICE_MSG_MAIN_AGENT_TOKEN:110,
|
||||
SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST:111,
|
||||
SPICE_MSG_MAIN_MIGRATE_END:112,
|
||||
SPICE_MSG_END_MAIN:113,
|
||||
SPICE_MSG_PLAYBACK_DATA:101,
|
||||
SPICE_MSG_PLAYBACK_MODE:102,
|
||||
SPICE_MSG_PLAYBACK_START:103,
|
||||
SPICE_MSG_PLAYBACK_STOP:104,
|
||||
SPICE_MSG_PLAYBACK_VOLUME:105,
|
||||
SPICE_MSG_PLAYBACK_MUTE:106,
|
||||
SPICE_MSG_END_PLAYBACK:107,
|
||||
SPICE_MSGC_RECORD_DATA:101,
|
||||
SPICE_MSGC_RECORD_MODE:102,
|
||||
SPICE_MSGC_RECORD_START_MARK:103,
|
||||
SPICE_MSGC_END_RECORD:104,
|
||||
SPICE_MSG_TUNNEL_INIT:101,
|
||||
SPICE_MSG_TUNNEL_SERVICE_IP_MAP:102,
|
||||
SPICE_MSG_TUNNEL_SOCKET_OPEN:103,
|
||||
SPICE_MSG_TUNNEL_SOCKET_FIN:104,
|
||||
SPICE_MSG_TUNNEL_SOCKET_CLOSE:105,
|
||||
SPICE_MSG_TUNNEL_SOCKET_DATA:106,
|
||||
SPICE_MSG_TUNNEL_SOCKET_CLOSED_ACK:107,
|
||||
SPICE_MSG_TUNNEL_SOCKET_TOKEN:108,
|
||||
SPICE_MSG_END_TUNNEL:109,
|
||||
SPICE_MSGC_TUNNEL_SERVICE_ADD:101,
|
||||
SPICE_MSGC_TUNNEL_SERVICE_REMOVE:102,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_OPEN_ACK:103,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_OPEN_NACK:104,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_FIN:105,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_CLOSED:106,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_CLOSED_ACK:107,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_DATA:108,
|
||||
SPICE_MSGC_TUNNEL_SOCKET_TOKEN:109,
|
||||
SPICE_MSGC_END_TUNNEL:110,
|
||||
SPICE_MSG_SMARTCARD_DATA:101,
|
||||
SPICE_MSG_END_SMARTCARD:102,
|
||||
SPICE_MSG_SPICEVMC_DATA:101,
|
||||
SPICE_MSG_END_SPICEVMC:102,
|
||||
SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION:0,
|
||||
SPICE_COMMON_CAP_AUTH_SPICE:1,
|
||||
SPICE_COMMON_CAP_AUTH_SASL:2,
|
||||
SPICE_COMMON_CAP_MINI_HEADER:3,
|
||||
SPICE_PLAYBACK_CAP_CELT_0_5_1:0,
|
||||
SPICE_PLAYBACK_CAP_VOLUME:1,
|
||||
SPICE_RECORD_CAP_CELT_0_5_1:0,
|
||||
SPICE_RECORD_CAP_VOLUME:1,
|
||||
SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE:0
|
||||
}
|
||||
|
||||
wdi.SpiceTunnelServiceType = {
|
||||
SPICE_TUNNEL_SERVICE_TYPE_INVALID:0,
|
||||
SPICE_TUNNEL_SERVICE_TYPE_GENERIC:1,
|
||||
SPICE_TUNNEL_SERVICE_TYPE_IPP:2,
|
||||
SPICE_TUNNEL_SERVICE_TYPE_ENUM_END:3
|
||||
}
|
||||
|
||||
wdi.SpiceJpegAlphaFlags = {
|
||||
SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN:1,
|
||||
SPICE_JPEG_ALPHA_FLAGS_MASK:1
|
||||
}
|
||||
|
||||
wdi.SpiceMaskFlags = {
|
||||
SPICE_MASK_FLAGS_INVERS:1,
|
||||
SPICE_MASK_FLAGS_MASK:1
|
||||
}
|
||||
|
||||
wdi.SpiceCursorType = {
|
||||
SPICE_CURSOR_TYPE_ALPHA:0,
|
||||
SPICE_CURSOR_TYPE_MONO:1,
|
||||
SPICE_CURSOR_TYPE_COLOR4:2,
|
||||
SPICE_CURSOR_TYPE_COLOR8:3,
|
||||
SPICE_CURSOR_TYPE_COLOR16:4,
|
||||
SPICE_CURSOR_TYPE_COLOR24:5,
|
||||
SPICE_CURSOR_TYPE_COLOR32:6,
|
||||
SPICE_CURSOR_TYPE_ENUM_END:7,
|
||||
SPICE_CURSOR_TYPE_URL:8
|
||||
}
|
||||
|
||||
wdi.SpiceImageFlags = {
|
||||
SPICE_IMAGE_FLAGS_CACHE_ME:1,
|
||||
SPICE_IMAGE_FLAGS_HIGH_BITS_SET:2,
|
||||
SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME:4,
|
||||
SPICE_IMAGE_FLAGS_MASK:7
|
||||
}
|
||||
|
||||
wdi.SpiceAudioDataMode = {
|
||||
SPICE_AUDIO_DATA_MODE_INVALID:0,
|
||||
SPICE_AUDIO_DATA_MODE_RAW:1,
|
||||
SPICE_AUDIO_DATA_MODE_CELT_0_5_1:2,
|
||||
SPICE_AUDIO_DATA_MODE_ENUM_END:3
|
||||
}
|
||||
|
||||
wdi.SpiceAudioFmt = {
|
||||
SPICE_AUDIO_FMT_INVALID:0,
|
||||
SPICE_AUDIO_FMT_S16:1,
|
||||
SPICE_AUDIO_FMT_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpiceBitmapFmt = {
|
||||
SPICE_BITMAP_FMT_INVALID:0,
|
||||
SPICE_BITMAP_FMT_1BIT_LE:1,
|
||||
SPICE_BITMAP_FMT_1BIT_BE:2,
|
||||
SPICE_BITMAP_FMT_4BIT_LE:3,
|
||||
SPICE_BITMAP_FMT_4BIT_BE:4,
|
||||
SPICE_BITMAP_FMT_8BIT:5,
|
||||
SPICE_BITMAP_FMT_16BIT:6,
|
||||
SPICE_BITMAP_FMT_24BIT:7,
|
||||
SPICE_BITMAP_FMT_32BIT:8,
|
||||
SPICE_BITMAP_FMT_RGBA:9,
|
||||
SPICE_BITMAP_FMT_ENUM_END:10
|
||||
}
|
||||
|
||||
wdi.SpiceStreamFlags = {
|
||||
SPICE_STREAM_FLAGS_TOP_DOWN:1,
|
||||
SPICE_STREAM_FLAGS_MASK:1
|
||||
}
|
||||
|
||||
wdi.SpiceTunnelIpType = {
|
||||
SPICE_TUNNEL_IP_TYPE_INVALID:0,
|
||||
SPICE_TUNNEL_IP_TYPE_IPv4:1,
|
||||
SPICE_TUNNEL_IP_TYPE_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpiceBitmapFlags = {
|
||||
SPICE_BITMAP_FLAGS_PAL_CACHE_ME:1,
|
||||
SPICE_BITMAP_FLAGS_PAL_FROM_CACHE:2,
|
||||
SPICE_BITMAP_FLAGS_TOP_DOWN:4,
|
||||
SPICE_BITMAP_FLAGS_MASK:7
|
||||
}
|
||||
|
||||
wdi.SpiceStringFlags = {
|
||||
SPICE_STRING_FLAGS_RASTER_A1:1,
|
||||
SPICE_STRING_FLAGS_RASTER_A4:2,
|
||||
SPICE_STRING_FLAGS_RASTER_A8:4,
|
||||
SPICE_STRING_FLAGS_RASTER_TOP_DOWN:8,
|
||||
SPICE_STRING_FLAGS_MASK:15
|
||||
}
|
||||
|
||||
wdi.SpiceSurfaceFmt = {
|
||||
SPICE_SURFACE_FMT_INVALID:0,
|
||||
SPICE_SURFACE_FMT_1_A:1,
|
||||
SPICE_SURFACE_FMT_8_A:8,
|
||||
SPICE_SURFACE_FMT_16_555:16,
|
||||
SPICE_SURFACE_FMT_32_xRGB:32,
|
||||
SPICE_SURFACE_FMT_16_565:80,
|
||||
SPICE_SURFACE_FMT_32_ARGB:96,
|
||||
SPICE_SURFACE_FMT_ENUM_END:97
|
||||
}
|
||||
|
||||
wdi.SpiceCursorFlags = {
|
||||
SPICE_CURSOR_FLAGS_NONE:1,
|
||||
SPICE_CURSOR_FLAGS_CACHE_ME:2,
|
||||
SPICE_CURSOR_FLAGS_FROM_CACHE:4,
|
||||
SPICE_CURSOR_FLAGS_MASK:7
|
||||
}
|
||||
|
||||
wdi.SpiceLinkErr = {
|
||||
SPICE_LINK_ERR_OK:0,
|
||||
SPICE_LINK_ERR_ERROR:1,
|
||||
SPICE_LINK_ERR_INVALID_MAGIC:2,
|
||||
SPICE_LINK_ERR_INVALID_DATA:3,
|
||||
SPICE_LINK_ERR_VERSION_MISMATCH:4,
|
||||
SPICE_LINK_ERR_NEED_SECURED:5,
|
||||
SPICE_LINK_ERR_NEED_UNSECURED:6,
|
||||
SPICE_LINK_ERR_PERMISSION_DENIED:7,
|
||||
SPICE_LINK_ERR_BAD_CONNECTION_ID:8,
|
||||
SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE:9,
|
||||
SPICE_LINK_ERR_ENUM_END:10
|
||||
}
|
||||
|
||||
wdi.SpiceNotifySeverity = {
|
||||
SPICE_NOTIFY_SEVERITY_INFO:0,
|
||||
SPICE_NOTIFY_SEVERITY_WARN:1,
|
||||
SPICE_NOTIFY_SEVERITY_ERROR:2,
|
||||
SPICE_NOTIFY_SEVERITY_ENUM_END:3
|
||||
}
|
||||
|
||||
wdi.SpiceBrushType = {
|
||||
SPICE_BRUSH_TYPE_NONE:0,
|
||||
SPICE_BRUSH_TYPE_SOLID:1,
|
||||
SPICE_BRUSH_TYPE_PATTERN:2,
|
||||
SPICE_BRUSH_TYPE_ENUM_END:3
|
||||
}
|
||||
|
||||
wdi.SpiceAlphaFlags = {
|
||||
SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA:1,
|
||||
SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA:2,
|
||||
SPICE_ALPHA_FLAGS_MASK:3
|
||||
}
|
||||
|
||||
wdi.SpiceSurfaceFlags = {
|
||||
SPICE_SURFACE_FLAGS_PRIMARY:1,
|
||||
SPICE_SURFACE_FLAGS_MASK:1
|
||||
}
|
||||
|
||||
wdi.QuicImageType = {
|
||||
QUIC_IMAGE_TYPE_INVALID: 0,
|
||||
QUIC_IMAGE_TYPE_GRAY: 1,
|
||||
QUIC_IMAGE_TYPE_RGB16: 2,
|
||||
QUIC_IMAGE_TYPE_RGB24: 3,
|
||||
QUIC_IMAGE_TYPE_RGB32: 4,
|
||||
QUIC_IMAGE_TYPE_RGBA: 5
|
||||
}
|
||||
|
||||
wdi.SpiceImageType = {
|
||||
SPICE_IMAGE_TYPE_BITMAP:0,
|
||||
SPICE_IMAGE_TYPE_QUIC:1,
|
||||
SPICE_IMAGE_TYPE_RESERVED:2,
|
||||
SPICE_IMAGE_TYPE_LZ_PLT:100,
|
||||
SPICE_IMAGE_TYPE_LZ_RGB:101,
|
||||
SPICE_IMAGE_TYPE_GLZ_RGB:102,
|
||||
SPICE_IMAGE_TYPE_FROM_CACHE:103,
|
||||
SPICE_IMAGE_TYPE_SURFACE:104,
|
||||
SPICE_IMAGE_TYPE_JPEG:105,
|
||||
SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS:106,
|
||||
SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB:107,
|
||||
SPICE_IMAGE_TYPE_JPEG_ALPHA:108,
|
||||
SPICE_IMAGE_TYPE_CANVAS:109,
|
||||
SPICE_IMAGE_TYPE_PNG:110,
|
||||
SPICE_IMAGE_TYPE_ENUM_END:111
|
||||
}
|
||||
|
||||
wdi.SpiceImageScaleMode = {
|
||||
SPICE_IMAGE_SCALE_MODE_INTERPOLATE:0,
|
||||
SPICE_IMAGE_SCALE_MODE_NEAREST:1,
|
||||
SPICE_IMAGE_SCALE_MODE_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpiceResourceType = {
|
||||
SPICE_RES_TYPE_INVALID:0,
|
||||
SPICE_RES_TYPE_PIXMAP:1,
|
||||
SPICE_RESOURCE_TYPE_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpicePathFlags = {
|
||||
SPICE_PATH_BEGIN:1,
|
||||
SPICE_PATH_END:2,
|
||||
SPICE_PATH_CLOSE:8,
|
||||
SPICE_PATH_BEZIER:16,
|
||||
SPICE_PATH_FLAGS_MASK:27
|
||||
}
|
||||
|
||||
wdi.SpiceVideoCodecType = {
|
||||
SPICE_VIDEO_CODEC_TYPE_MJPEG:1,
|
||||
SPICE_VIDEO_CODEC_TYPE_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpiceRopd = {
|
||||
SPICE_ROPD_INVERS_SRC:1,
|
||||
SPICE_ROPD_INVERS_BRUSH:2,
|
||||
SPICE_ROPD_INVERS_DEST:4,
|
||||
SPICE_ROPD_OP_PUT:8,
|
||||
SPICE_ROPD_OP_OR:16,
|
||||
SPICE_ROPD_OP_AND:32,
|
||||
SPICE_ROPD_OP_XOR:64,
|
||||
SPICE_ROPD_OP_BLACKNESS:128,
|
||||
SPICE_ROPD_OP_WHITENESS:256,
|
||||
SPICE_ROPD_OP_INVERS:512,
|
||||
SPICE_ROPD_INVERS_RES:1024,
|
||||
SPICE_ROPD_MASK:2047
|
||||
}
|
||||
|
||||
wdi.SpiceMigrateFlags = {
|
||||
SPICE_MIGRATE_NEED_FLUSH:1,
|
||||
SPICE_MIGRATE_NEED_DATA_TRANSFER:2,
|
||||
SPICE_MIGRATE_FLAGS_MASK:3
|
||||
}
|
||||
|
||||
wdi.SpiceKeyboardModifierFlags = {
|
||||
SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK:1,
|
||||
SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK:2,
|
||||
SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK:4,
|
||||
SPICE_KEYBOARD_MODIFIER_FLAGS_MASK:7
|
||||
}
|
||||
|
||||
wdi.SpiceInfoCode = {
|
||||
SPICE_INFO_GENERAL:0,
|
||||
SPICE_INFO_CODE_ENUM_END:1
|
||||
}
|
||||
|
||||
wdi.SpiceMouseButton = {
|
||||
SPICE_MOUSE_BUTTON_INVALID:0,
|
||||
SPICE_MOUSE_BUTTON_LEFT:1,
|
||||
SPICE_MOUSE_BUTTON_MIDDLE:2,
|
||||
SPICE_MOUSE_BUTTON_RIGHT:3,
|
||||
SPICE_MOUSE_BUTTON_UP:4,
|
||||
SPICE_MOUSE_BUTTON_DOWN:5,
|
||||
SPICE_MOUSE_BUTTON_ENUM_END:6
|
||||
}
|
||||
|
||||
wdi.SpiceClipType = {
|
||||
SPICE_CLIP_TYPE_NONE:0,
|
||||
SPICE_CLIP_TYPE_RECTS:1,
|
||||
SPICE_CLIP_TYPE_ENUM_END:2
|
||||
}
|
||||
|
||||
wdi.SpiceMouseButtonMask = {
|
||||
SPICE_MOUSE_BUTTON_MASK_LEFT:1,
|
||||
SPICE_MOUSE_BUTTON_MASK_MIDDLE:2,
|
||||
SPICE_MOUSE_BUTTON_MASK_RIGHT:4,
|
||||
SPICE_MOUSE_BUTTON_MASK_MASK:7
|
||||
}
|
||||
|
||||
wdi.SpiceMouseModeTypes = {
|
||||
SPICE_MOUSE_MODE_SERVER:1,
|
||||
SPICE_MOUSE_MODE_CLIENT:2,
|
||||
SPICE_MOUSE_MODE_MASK:3
|
||||
}
|
||||
|
||||
wdi.AgentCaps = {
|
||||
VD_AGENT_CAP_MOUSE_STATE: 0,
|
||||
VD_AGENT_CAP_MONITORS_CONFIG: 1,
|
||||
VD_AGENT_CAP_REPLY: 2,
|
||||
VD_AGENT_CAP_CLIPBOARD: 3,
|
||||
VD_AGENT_CAP_DISPLAY_CONFIG: 4,
|
||||
VD_AGENT_CAP_CLIPBOARD_BY_DEMAND: 5,
|
||||
VD_AGENT_CAP_CLIPBOARD_SELECTION : 6
|
||||
};
|
||||
|
||||
wdi.AgentMessageTypes = {
|
||||
VD_AGENT_MOUSE_STATE:1,
|
||||
VD_AGENT_MONITORS_CONFIG: 2,
|
||||
VD_AGENT_REPLY: 3,
|
||||
VD_AGENT_CLIPBOARD: 4,
|
||||
VD_AGENT_DISPLAY_CONFIG: 5,
|
||||
VD_AGENT_ANNOUNCE_CAPABILITIES: 6,
|
||||
VD_AGENT_CLIPBOARD_GRAB: 7,
|
||||
VD_AGENT_CLIPBOARD_REQUEST: 8,
|
||||
VD_AGENT_CLIPBOARD_RELEASE: 9,
|
||||
|
||||
VD_AGENT_GET_WINDOWS_LIST: 10,
|
||||
VD_AGENT_CLOSE_WINDOW: 11,
|
||||
VD_AGENT_MOVE_WINDOW: 12,
|
||||
VD_AGENT_RESIZE_WINDOW: 13,
|
||||
VD_AGENT_MINIMIZE_WINDOW: 14,
|
||||
VD_AGENT_RESTORE_WINDOW: 15,
|
||||
VD_AGENT_MAXIMIZE_WINDOW: 16,
|
||||
VD_AGENT_FOCUS_WINDOW: 17,
|
||||
VD_AGENT_EXECUTE_COMMAND: 18
|
||||
};
|
||||
|
||||
wdi.ClipBoardTypes = {
|
||||
VD_AGENT_CLIPBOARD_NONE: 0,
|
||||
VD_AGENT_CLIPBOARD_UTF8_TEXT: 1,
|
||||
VD_AGENT_CLIPBOARD_IMAGE_PNG: 2, /* All clients with image support should support this one */
|
||||
VD_AGENT_CLIPBOARD_IMAGE_BMP: 3, /* optional */
|
||||
VD_AGENT_CLIPBOARD_IMAGE_TIFF: 4, /* optional */
|
||||
VD_AGENT_CLIPBOARD_IMAGE_JPG: 5 /* optional */
|
||||
};
|
3312
js/spice-web-client/spiceobjects/spiceobjects.js
Normal file
3312
js/spice-web-client/spiceobjects/spiceobjects.js
Normal file
File diff suppressed because it is too large
Load Diff
2
js/spice-web-client/spiceproxy/.gitignore
vendored
Normal file
2
js/spice-web-client/spiceproxy/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/spiceproxy.js
|
||||
/*.tgz
|
0
js/spice-web-client/spiceproxy/.npmignore
Normal file
0
js/spice-web-client/spiceproxy/.npmignore
Normal file
46
js/spice-web-client/spiceproxy/concatenator.js
Executable file
46
js/spice-web-client/spiceproxy/concatenator.js
Executable file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// change to parent directory so all paths are relative to the top dir
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
var targetFile = path.join(__dirname, 'spiceproxy.js');
|
||||
|
||||
var files = [
|
||||
'lib/utils.js',
|
||||
'spiceobjects/spiceobjects.js',
|
||||
'spiceobjects/generated/protocol.js',
|
||||
'lib/GlobalPool.js',
|
||||
'lib/GenericObjectPool.js',
|
||||
'spiceproxy/socket.js',
|
||||
'spiceproxy/globalpool.js',
|
||||
'lib/queue.js',
|
||||
'network/socketqueue.js',
|
||||
'network/packetextractor.js',
|
||||
'network/packetcontroller.js',
|
||||
'network/sizedefiner.js',
|
||||
'network/packetreassembler.js',
|
||||
'network/reassemblerfactory.js',
|
||||
'lib/biginteger.js',
|
||||
'spiceproxy/spicechannel.js'
|
||||
];
|
||||
|
||||
var exportString = "\nmodule.exports = wdi; \n";
|
||||
|
||||
console.log("Will generate %s", targetFile);
|
||||
if (fs.existsSync(targetFile)) {
|
||||
fs.unlinkSync(targetFile);
|
||||
}
|
||||
|
||||
files.forEach(function (file) {
|
||||
var data = fs.readFileSync(file);
|
||||
console.log('... appending %s', file);
|
||||
fs.appendFileSync(targetFile, data);
|
||||
});
|
||||
|
||||
console.log("Done! Appending module.exports line...");
|
||||
fs.appendFileSync(targetFile, exportString);
|
||||
|
||||
console.log("Finish... Everything is stored in %s", targetFile);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user