blob: 4ac2fb7ed2c4852ea2738b347fe597b3a6ce4fe9 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.net
{
COMPILE::SWF
{
import flash.net.SharedObject;
import flash.events.Event;
import flash.events.NetStatusEvent;
import flash.events.Event;
}
COMPILE::JS{
import org.apache.royale.net.remoting.amf.AMFBinaryData;
}
import mx.events.NetStatusEvent;
import org.apache.royale.events.EventDispatcher;
/**
* An emulation class to support the swf based Local SharedObject support.
* This implementation supports AMF encoded content (requires registerClassAlias before reading and writing to roundtrip instances of custom classes)
*/
public class SharedObject extends org.apache.royale.events.EventDispatcher
{
private static const map:Object = {};
private static var unlocked:Boolean;
public var _size:uint = 0;
public function get size():uint {
return _size;
}
public static function getLocal(name:String, localPath:String = null, secure:Boolean = false):mx.net.SharedObject
{
var pathKey:String = localPath == null ? '$null$' : localPath;
COMPILE::JS {
localPath = pathKey;
}
var cached:mx.net.SharedObject = map[pathKey + '::' + name];
if (!cached)
{
unlocked = true;
cached = new mx.net.SharedObject();
unlocked = false;
map[pathKey + '::' + name] = cached;
cached.setName(name);
cached.setLocalPath(localPath);
cached.createSO(secure);
}
COMPILE::JS{
if (!map['#']) {
window.addEventListener('pagehide', unloadHandler);
map['#'] = true;
}
}
return cached;
}
COMPILE::JS
private static function unloadHandler(event:PageTransitionEvent):void{
//@todo consider whether we do anything different if event.persisted is true or false
for (var key:String in map) {
if (key != '#') {
var so:SharedObject = map[key];
//@todo what to do with errors here:
so.flush();
}
}
}
public function SharedObject()
{
if (!unlocked) throw new Error('ArgumentError: Error #2012: SharedObject class cannot be instantiated.')
}
COMPILE::SWF
private var _so:flash.net.SharedObject;
COMPILE::JS
private var _ls:Storage;
/**
*
* @param minDiskSpace ignored for javascript targets
* @return a string indicating the flush status. This can be (on swf) SharedObjectFlushStatus.FLUSHED or SharedObjectFlushStatus.PENDING on swf. For JS is it SharedObjectFlushStatus.FLUSHED or SharedObjectFlushStatus.FAILED
*
* @throws Error #2044: Unhandled NetStatusEvent
*/
public function flush(minDiskSpace:int = 0):String
{
COMPILE::JS
{
if (_data)
{
try{
var amf:AMFBinaryData = new AMFBinaryData();
amf.writeObject(_data);
var base64:String = window['btoa'](String.fromCharCode.apply(null, amf.array));
_ls.setItem(_localPath + "::" + _name, base64);
} catch(e:Error) {
if (hasEventListener(NetStatusEvent.NET_STATUS)) {
var event:NetStatusEvent = new NetStatusEvent(NetStatusEvent.NET_STATUS);
event.info = {"code":"SharedObject.Flush.Failed","level":"error"};
dispatchEvent(event);
return SharedObjectFlushStatus.FAILED;
} else {
throw new Error('Error #2044: Unhandled NetStatusEvent:. level=error, code=SharedObject.Flush.Failed');
}
}
}
//js never returns pending, because there is no way for the user to accept or decline the byte size storage limits
return SharedObjectFlushStatus.FLUSHED;
}
COMPILE::SWF
{
return _so.flush(minDiskSpace);
}
}
public function clear():void{
COMPILE::SWF{
_so.clear();
}
COMPILE::JS{
if (_data) {
if (_ls) {
_ls.removeItem(_localPath + "::" + _name);
}
_data = null;
}
}
}
COMPILE::JS
private var _data:Object;
public function get data():Object
{
COMPILE::SWF
{
return _so.data;
}
COMPILE::JS
{
if (!_data)
{
_data = {};
}
return _data;
}
}
private var _name:String;
private function setName(name:String):void
{
COMPILE::JS{
//apply same limits as swf
if (/[~%&\\;:"',<>?#\s]/.test(name)) throw new Error('Error #2134: Cannot create SharedObject.')
}
_name = name;
}
private var _localPath:String;
private function setLocalPath(localPath:String):void
{
_localPath = localPath;
}
COMPILE::SWF
private function redispatch(event:flash.events.Event):void{
if (event is flash.events.NetStatusEvent) {
var nse:flash.events.NetStatusEvent = flash.events.NetStatusEvent(event);
var mxnse:mx.events.NetStatusEvent = new mx.events.NetStatusEvent(nse.type,false, false, nse.info);
dispatchEvent(mxnse);
} else {
//just redispatch?
dispatchEvent(event.clone());
}
}
private function createSO(secure:Boolean):void
{
COMPILE::SWF{
_so = flash.net.SharedObject.getLocal(_name, _localPath, secure);
_so.addEventListener('netStatus', redispatch);
_so.addEventListener('asyncError', redispatch); //not sure about this one for LSO..
//_so.addEventListener('sync', redispatch); //only relevant for RSO, not LSO
}
COMPILE::JS{
_ls = window.localStorage;
if (!_ls && typeof Storage != "undefined") {
//this gets around an issue with local testing with file:// protocol in IE11
var p:String = window.location.pathname.replace(/(^..)(:)/, "$1$$");
window.location.href = window.location.protocol + "//127.0.0.1" + p;
_ls = window.localStorage;
}
if (!_ls) {
throw new Error('local storage not supported');
}
var base64:String = _ls.getItem(_localPath + "::" + _name);
if (base64)
{
var binary_string:String = window['atob'](base64);
var arr:Uint8Array = new Uint8Array(
binary_string.split('')
.map(
function (charStr:String):uint
{
return charStr.charCodeAt(0);
}
)
);
var amf:AMFBinaryData = new AMFBinaryData(arr.buffer);
_data = amf.readObject();
}
}
}
}
}