// Utility functions
define(["webL10n","sugar-web/datastore","FileSaver"], function (l10n, datastore, FileSaver) {
var util = {};
var units = [{name:'Years', factor:356 * 24 * 60 * 60},
{name:'Months', factor:30 * 24 * 60 * 60},
{name:'Weeks', factor:7 * 24 * 60 * 60},
{name:'Days', factor:24 * 60 * 60},
{name:'Hours', factor:60 * 60},
{name:'Minutes', factor:60}];
// Port of timestamp elapsed string from Sugar - timestamp is in milliseconds elapsed since 1er january 1970
util.timestampToElapsedString = function(timestamp, maxlevel, issmall) {
var suffix = (issmall ? "_short" : "");
var levels = 0;
var time_period = '';
var elapsed_seconds = ((new Date().getTime()) - timestamp)/1000;
for (var i = 0; i < units.length ; i++) {
var factor = units[i].factor;
var elapsed_units = Math.floor(elapsed_seconds / factor);
if (elapsed_units > 0) {
if (levels > 0)
time_period += ',';
time_period += ' '+elapsed_units+" "+(elapsed_units==1?l10n.get(units[i].name+"_one"+suffix):l10n.get(units[i].name+"_other"+suffix));
elapsed_seconds -= elapsed_units * factor;
}
if (time_period != '')
levels += 1;
if (levels == maxlevel)
break;
}
if (levels == 0) {
return l10n.get("SecondsAgo"+suffix);
}
return l10n.get("Ago"+suffix, {time:time_period});
}
// Port of get_date_range from Sugar
util.getDateRange = function(rangetype) {
if (rangetype === undefined) {
return null;
}
var now = new Date();
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
now = now.getTime();
var range = null;
switch(rangetype) {
case 1:
range = { min: today, max: now }; // TODAY
break;
case 2:
range = { min: today-86400000, max: now }; // YESTERDAY
break;
case 3:
range = { min: today-604800000, max: now }; // PAST WEEK
break;
case 4:
range = { min: today-2592000000, max: now }; // PAST MONTH
break;
case 5:
range = { min: today-30758400000, max: now }; // PAST YEAR
break;
}
return range;
}
// Port of get_next/previous_stroke/fill_color from Sugar
util.getNextStrokeColor = function(color) {
var current = util.getColorIndex(color);
if (current == -1) {
return color;
}
var next = nextIndex(current);
while (xoPalette.colors[next].stroke != xoPalette.colors[current].stroke) {
next = nextIndex(next);
}
return xoPalette.colors[next];
}
util.getPreviousStrokeColor = function(color) {
var current = util.getColorIndex(color);
if (current == -1) {
return color;
}
var previous = previousIndex(current);
while (xoPalette.colors[previous].stroke != xoPalette.colors[current].stroke) {
previous = previousIndex(previous);
}
return xoPalette.colors[previous];
}
util.getNextFillColor = function(color) {
var current = util.getColorIndex(color);
if (current == -1) {
return color;
}
var next = nextIndex(current);
while (xoPalette.colors[next].fill != xoPalette.colors[current].fill) {
next = nextIndex(next);
}
return xoPalette.colors[next];
}
util.getPreviousFillColor = function(color) {
var current = util.getColorIndex(color);
if (current == -1) {
return color;
}
var previous = previousIndex(current);
while (xoPalette.colors[previous].fill != xoPalette.colors[current].fill) {
previous = previousIndex(previous);
}
return xoPalette.colors[previous];
}
util.getColorIndex = function(color) {
for (var i = 0 ; i < xoPalette.colors.length ; i++) {
if (color.stroke == xoPalette.colors[i].stroke && color.fill == xoPalette.colors[i].fill) {
return i;
}
}
return -1;
}
function nextIndex(index) {
var next = index + 1;
if (next == xoPalette.colors.length) {
next = 0;
}
return next;
}
function previousIndex(index) {
var previous = index - 1;
if (previous < 0) {
previous = xoPalette.colors.length - 1;
}
return previous;
}
// Get center of screen in canvas
util.getCanvasCenter = function() {
var canvas = document.getElementById("canvas") || document.getElementById("body");
var canvas_height = canvas.offsetHeight;
var canvas_width = canvas.offsetWidth;
var canvas_centery = parseFloat(canvas_height)/2.0;
var canvas_centerx = parseFloat(canvas_width)/2.0
return { x: canvas_centerx, y: canvas_centery, dx: canvas_width, dy: canvas_height };
}
// Compute margin for the element to be centered
util.computeMargin = function(size, maxpercent) {
var canvas = document.getElementById("canvas");
var canvas_height = canvas.offsetHeight;
var canvas_width = canvas.offsetWidth;
var size_width = (size.width <= canvas_width ? size.width : maxpercent.width*canvas_width);
var size_height = (size.height <= canvas_height ? size.height : maxpercent.height*canvas_height);
return { left: parseFloat(canvas_width-size_width)/2.0, top: parseFloat(canvas_height-size_height)/2.0, width: size_width, height: size_height };
}
// Test if cursor is inside element
util.cursorIsInside = function(ctrl) {
var obj = document.getElementById(ctrl.getAttribute("id"));
if (obj == null) return false;
var p = {};
p.x = obj.offsetLeft;
p.y = obj.offsetTop;
p.dx = obj.clientWidth;
p.dy = obj.clientHeight;
while (obj.offsetParent) {
p.x = p.x + obj.offsetParent.offsetLeft;
p.y = p.y + obj.offsetParent.offsetTop - obj.scrollTop;
if (obj == document.getElementsByTagName("body")[0]) {
break;
} else {
obj = obj.offsetParent;
}
}
var isInside = (
mouse.position.x >= p.x && mouse.position.x <= p.x + p.dx
&& mouse.position.y >= p.y && mouse.position.y <= p.y + p.dy
);
return isInside;
};
// Show/Hide toolbar
util.setToolbar = function(newtoolbar) {
if (toolbar == newtoolbar) {
return;
}
toolbar = newtoolbar;
newtoolbar.renderInto(document.getElementById("toolbar"));
}
// Get browser name
util.getBrowserName = function() {
if (enyo.platform.android) return "Android";
else if (enyo.platform.androidChrome) return "Chrome Android";
else if (enyo.platform.androidFirefox) return "Firefox Android";
else if (enyo.platform.ie) return "IE";
else if (enyo.platform.ios) return "iOS";
else if (enyo.platform.webos) return "webOS";
else if (enyo.platform.blackberry) return "BlackBerry";
else if (enyo.platform.safari) return "Safari";
else if (enyo.platform.chrome) return "Chrome";
else if (enyo.platform.firefox) return "Firefox";
return "Unknown";
}
// Get browser version
util.getBrowserVersion = function() {
if (enyo.platform.android) return enyo.platform.android;
else if (enyo.platform.androidChrome) return enyo.platform.androidChrome;
else if (enyo.platform.androidFirefox) return enyo.platform.androidFirefox;
else if (enyo.platform.ie) return enyo.platform.ie;
else if (enyo.platform.ios) return enyo.platform.ios;
else if (enyo.platform.webos) return enyo.platform.webos;
else if (enyo.platform.blackberry) return enyo.platform.blackberry;
else if (enyo.platform.safari) return enyo.platform.safari;
else if (enyo.platform.chrome) return enyo.platform.chrome;
else if (enyo.platform.firefox) return enyo.platform.firefox;
return "?";
}
// Get client type
util.getClientType = function() {
return (document.location.protocol.substr(0,4) == "http" && !constant.noServerMode) ? constant.webAppType : constant.appType;
}
util.getClientName = function() {
return this.getClientType() == constant.webAppType ? "Web App" : "App";
}
// URL location
util.getCurrentServerUrl = function() {
var url = window.location.href;
return url.substring(0, url.lastIndexOf('/'));
}
// Min password size
util.getMinPasswordSize = function() {
var minSize = constant.minPasswordSize;
var info = preferences.getServer();
if (info && info.options && info.options["min-password-size"]) {
minSize = info.options["min-password-size"];
}
return minSize;
}
// Restart application
util.restartApp = function() {
location.reload();
}
// Vibrate slightly the device
util.vibrate = function() {
if ((enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) && util.getClientType() == constant.appType && navigator.vibrate) {
navigator.vibrate(30);
}
}
// Hide native toolbar
util.hideNativeToolbar = function() {
if ((enyo.platform.android || enyo.platform.androidChrome) && util.getClientType() == constant.appType && !constant.noServerMode) {
AndroidFullScreen.immersiveMode(function() {}, function() {});
}
}
// Handle volume buttons
util.handleVolumeButtons = function() {
if ((enyo.platform.android || enyo.platform.androidChrome) && util.getClientType() == constant.appType && !constant.noServerMode) {
// HACK: Need only on Android because Cordova intercept volume buttons
var emptyf = function() {};
document.addEventListener("volumeupbutton", function() {
cordova.plugins.VolumeControl.getVolume(function(value) {
var volume = parseInt(value);
if (volume < 100) {
cordova.plugins.VolumeControl.setVolume((volume+10), emptyf, emptyf);
}
}, emptyf);
}, false);
document.addEventListener("volumedownbutton", function() {
cordova.plugins.VolumeControl.getVolume(function(value) {
var volume = parseInt(value);
if (volume > 0) {
cordova.plugins.VolumeControl.setVolume((volume-1), emptyf, emptyf);
}
}, emptyf);
}, false);
}
}
// Change browser fav icon and title
var buddyIcon='\
';
util.updateFavicon = function() {
var color = preferences.getColor();
var name = preferences.getName();
if (!color) {
color = {stroke: "#005FE4", fill: "#FF2B34"};
}
document.title = ((name&&name!="")?name+" - ":"")+"Sugarizer";
var icon = buddyIcon.replace(new RegExp("&fill_color;","g"),color.fill).replace(new RegExp("&stroke_color;","g"),color.stroke);
var svg_xml = (new XMLSerializer()).serializeToString((new DOMParser()).parseFromString(icon, "text/xml"));
var canvas = document.createElement('canvas');
canvas.width = 16;
canvas.height = 16;
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = "data:image/svg+xml;base64," + btoa(svg_xml);
img.onload = function() {
ctx.drawImage(img, 0, 0);
var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.href = canvas.toDataURL("image/x-icon");
document.getElementsByTagName('head')[0].appendChild(link);
}
}
// Scan code functions
var currentCamera = 0;
var qrCancelCallback = null;
function closeQR() {
QRScanner.cancelScan(function(status){});
if (qrCancelCallback) {
qrCancelCallback();
}
document.getElementById("toolbar").style.opacity = 1;
document.getElementById("canvas").style.opacity = 1;
document.getElementById("qrclosebutton").style.visibility = "hidden";
document.getElementById("qrswitchbutton").style.visibility = "hidden";
var qrBackground = document.getElementById("qrbackground");
qrBackground.parentNode.removeChild(qrBackground);
}
function switchCameraQR() {
currentCamera = (currentCamera + 1) % 2;
QRScanner.useCamera(currentCamera, function(err, status){});
}
util.scanQRCode = function(okCallback, cancelCallback) {
currentCamera = 0;
qrCancelCallback = cancelCallback;
var qrBackground = document.createElement('div');
qrBackground.className = "first-qrbackground";
qrBackground.id = "qrbackground";
document.getElementById("canvas").appendChild(qrBackground);
document.getElementById("qrclosebutton").addEventListener('click', closeQR);
document.getElementById("qrswitchbutton").addEventListener('click', switchCameraQR);
QRScanner.prepare(function(err, status) {
document.getElementById("toolbar").style.opacity = 0;
document.getElementById("canvas").style.opacity = 0;
document.getElementById("qrclosebutton").style.visibility = "visible";
document.getElementById("qrswitchbutton").style.visibility = "visible";
if (err) {
console.log("Error "+err);
} else {
QRScanner.scan(function(err, code) {
if (err) {
console.log("Error "+err);
} else {
if (okCallback) {
okCallback(code);
}
}
util.cancelScan();
document.getElementById("toolbar").style.opacity = 1;
document.getElementById("canvas").style.opacity = 1;
document.getElementById("qrclosebutton").style.visibility = "hidden";
document.getElementById("qrswitchbutton").style.visibility = "hidden";
});
QRScanner.show(function(status) {});
}
});
}
util.cancelScan = function() {
QRScanner.cancelScan(function(status){});
qrCancelCallback = null;
document.getElementById("qrclosebutton").removeEventListener('click', closeQR);
document.getElementById("qrswitchbutton").removeEventListener('click', switchCameraQR);
var qrBackground = document.getElementById("qrbackground");
if (qrBackground) qrBackground.parentNode.removeChild(qrBackground);
}
// Decoding functions taken from
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
function b64ToUint6(nChr) {
return nChr > 64 && nChr < 91 ?
nChr - 65
: nChr > 96 && nChr < 123 ?
nChr - 71
: nChr > 47 && nChr < 58 ?
nChr + 4
: nChr === 43 ?
62
: nChr === 47 ?
63
:
0;
}
function base64DecToArr(sBase64, nBlocksSize) {
var
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}
// Write a new file
util.writeFile = function(directory, metadata, content, callback) {
var binary = null;
var text = null;
var extension = "json";
var title = metadata.title;
var mimetype = 'application/json';
if (metadata && metadata.mimetype) {
mimetype = metadata.mimetype;
if (mimetype == "image/jpeg") {
extension = "jpg";
} else if (mimetype == "image/png") {
extension = "png";
} else if (mimetype == "audio/wav") {
extension = "wav";
} else if (mimetype == "video/webm") {
extension = "webm";
} else if (mimetype == "audio/mp3"||mimetype == "audio/mpeg") {
extension = "mp3";
} else if (mimetype == "video/mp4") {
extension = "mp4";
} else if (mimetype == "text/plain") {
extension = "txt";
text = JSON.parse(content);
} else if (mimetype == "application/pdf") {
extension = "pdf";
} else if (mimetype == "application/msword") {
extension = "doc";
} else if (mimetype == "application/vnd.oasis.opendocument.text") {
extension = "odt";
} else {
extension = "bin";
}
binary = base64DecToArr(content.substr(content.indexOf('base64,')+7)).buffer;
} else {
text = JSON.stringify({metadata: metadata, text: content});
}
var filename = title;
if (filename.indexOf("."+extension)==-1) {
filename += "."+extension;
}
if (util.getClientType() == constant.appType && (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios || enyo.platform.electron) && !constant.noServerMode) {
if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
var cordovaFileStorage = cordova.file.externalRootDirectory;
if (enyo.platform.ios) {
cordovaFileStorage = cordova.file.documentsDirectory;
}
window.resolveLocalFileSystemURL(cordovaFileStorage, function(directory) {
directory.getFile(filename, {create:true}, function(file) {
if (!file) {
return;
}
file.createWriter(function(fileWriter) {
fileWriter.seek(fileWriter.length);
if (text) {
var blob = new Blob([text], {type:mimetype});
fileWriter.write(blob);
} else {
fileWriter.write(binary);
}
callback(null, file.fullPath);
}, function(evt) {
callback(error.code, null);
});
});
});
} else {
var electron = require("electron");
var ipc = electron.ipcRenderer;
ipc.removeAllListeners('save-file-reply');
ipc.send('save-file-dialog', {directory: directory, filename: filename, mimetype: mimetype, extension: extension, text: text, binary: content});
ipc.on('save-file-reply', function(event, arg) {
callback(arg.err, arg.filename);
});
}
} else {
var blob = new Blob((text?[text]:[binary]), {type:mimetype});
FileSaver.saveAs(blob, filename);
callback(null, filename);
}
}
// Write file content to datastore
function writeFileToStore(file, text, callback) {
if (file.type == 'application/json') {
// Handle JSON file
var data = null;
try {
data = JSON.parse(text);
if (!data.metadata) {
callback(file.name, -1);
return;
}
} catch(e) {
callback(file.name, -1);
return;
}
callback(file.name, 0, data.metadata, data.text);
} else {
var activity = "";
if (file.type != "text/plain" && file.type != "application/pdf" && file.type != "application/msword" && file.type != "application/vnd.oasis.opendocument.text") {
activity = "org.olpcfrance.MediaViewerActivity";
}
var metadata = {
title: file.name,
mimetype: file.type,
activity: activity
}
callback(file.name, 0, metadata, text);
}
}
// Load a file
util.loadFile = function(file, callback) {
// Use FileReaer object in web
if (util.getClientType() == constant.webAppType || constant.noServerMode || (util.getClientType() == constant.appType && !enyo.platform.android && !enyo.platform.androidChrome && !enyo.platform.ios && !enyo.platform.electron)) {
// Ensure type is valid
var validTypes = ['application/json','image/jpeg','image/png','audio/wav','video/webm','audio/mp3','audio/mpeg','video/mp4','text/plain','application/pdf','application/msword','application/vnd.oasis.opendocument.text'];
if (validTypes.indexOf(file.type) == -1) {
callback(file.name, -1);
return;
}
var reader = new FileReader();
reader.onload = function() {
writeFileToStore(file, reader.result, callback);
};
if (file.type == 'application/json') {
reader.readAsText(file);
} else {
reader.readAsDataURL(file);
}
}
}
// Ask the user a directory (use to save a set of files)
util.askDirectory = function(callback) {
if (util.getClientType() != constant.appType || (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios)) {
return;
}
var electron = require("electron");
var ipc = electron.ipcRenderer;
ipc.removeAllListeners('choose-directory-reply');
ipc.send('choose-directory-dialog');
ipc.on('choose-directory-reply', function(event, arg) {
callback(arg);
})
}
// Ask the user a set files and write it to datastore
util.askFiles = function(callback) {
if (util.getClientType() != constant.appType) {
return;
}
if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
if (!window.cordova && !window.PhoneGap) {
return;
}
var file = {
type: "image/jpeg",
name: l10n.get("ImageFromDevice")
};
var captureSuccess = function(imageData) {
var text = "data:image/jpeg;base64," + imageData;
writeFileToStore(file, text, callback);
}
var captureError = function(error) {
};
navigator.camera.getPicture(captureSuccess, captureError, {
quality: 50,
targetWidth: 640,
targetHeight: 480,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY
});
} else {
var electron = require("electron");
var ipc = electron.ipcRenderer;
ipc.removeAllListeners('choose-files-reply');
ipc.send('choose-files-dialog');
ipc.on('choose-files-reply', function(event, file, err, text) {
if (err) {
callback(file.name, -1);
} else {
writeFileToStore(file, text, callback);
}
});
}
}
function base64toBlob(mimetype, base64) {
var contentType = mimetype;
var byteCharacters = atob(base64.substr(base64.indexOf(';base64,')+8));
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += 1024) {
var slice = byteCharacters.slice(offset, offset + 1024);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
// Open the content as a document in a new Window
util.openAsDocument = function(metadata, text) {
if (util.getClientType() == constant.webAppType || (util.getClientType() == constant.appType && !enyo.platform.android && !enyo.platform.androidChrome && !enyo.platform.ios && !enyo.platform.electron) || constant.noServerMode) {
// Convert blob object URL
var blob = base64toBlob(metadata.mimetype, text);
var blobUrl = URL.createObjectURL(blob);
// Open in a new browser tab
window.open(blobUrl, '_blank');
} else if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
// Create a temporary file
var cordovaFileStorage = cordova.file.externalCacheDirectory;
if (enyo.platform.ios) {
cordovaFileStorage = cordova.file.documentsDirectory;
}
window.resolveLocalFileSystemURL(cordovaFileStorage, function(directory) {
directory.getFile("sugarizertemp", {create: true, exclusive: false}, function(file) {
if (!file) {
return;
}
file.createWriter(function(fileWriter) {
fileWriter.seek(fileWriter.length);
var blob = base64toBlob(metadata.mimetype, text)
fileWriter.write(blob);
// Open file in browser
if (enyo.platform.ios) {
// On iOS should be transfered in Cordova file space first
var fileTransfer = new FileTransfer();
var fileURL = "cdvfile://localhost/temporary/sugarizer/sugarizertemp";
fileTransfer.download(
cordovaFileStorage+"sugarizertemp",
fileURL,
function(entry) {
cordova.InAppBrowser.open(fileURL, '_blank', 'location=no,closebuttoncaption='+l10n.get("Ok"));
},
function(error) {
console.log("download error code " + error.code);
},
true
);
} else {
// On Android, just open in the file system
var filename = cordovaFileStorage+"sugarizertemp";
cordova.InAppBrowser.open(filename, '_system');
}
}, function(evt) {
});
});
});
} else {
// Save in a temporary file
var electron = require("electron");
var shell = electron.shell;
var ipc = electron.ipcRenderer;
ipc.removeAllListeners('create-tempfile-reply');
ipc.send('create-tempfile', {metadata: metadata, text: text});
ipc.on('create-tempfile-reply', function(event, file) {
// Open in a shell
shell.openExternal("file://"+file);
});
}
}
// Clean localStorage: datastore and settings
util.cleanDatastore = function(full, then) {
var results = datastore.find();
var i = 0;
var removeOneEntry = function(err) {
if (++i < results.length) {
datastore.remove(results[i].objectId, removeOneEntry);
} else {
if (then) {
then();
}
}
};
preferences.reset(full);
if (results.length) {
datastore.remove(results[i].objectId, removeOneEntry);
} else {
if (then) {
then();
}
}
}
// Compute storage size
util.computeDatastoreSize = function(callback) {
// Compute local storage size
var used = 0;
for(var x in localStorage) {
var amount = (localStorage[x].length * 2);
if (!isNaN(amount)) {
used += amount;
}
}
// Compute file size
var results = datastore.find();
for (var i = 0 ; i < results.length ; i++) {
var entry = results[i];
var textsize = entry.metadata["textsize"];
if (textsize) {
used += textsize;
}
}
if (callback) {
callback({bytes: used, formatted: util.formatSize(used)});
}
}
// Format size
util.formatSize = function(bytes) {
var formatted = "";
if (bytes > 1048576) {
formated = (bytes/1024/1024).toFixed()+" "+l10n.get("ShortForMegabytes");
} else if (bytes > 1024) {
formated = (bytes/1024).toFixed()+" "+l10n.get("ShortForKilobytes");
} else if (bytes == 0) {
formated = "-";
} else {
formated = bytes + " " + l10n.get("ShortForBytes");
}
return formated;
}
// Quit application
util.quitApp = function() {
new Sugar.EE({mode: 2}).renderInto(document.getElementById("body"));
window.setTimeout(function() {
if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) {
window.top.postMessage("", '*');
}
window.close();
if (!/Edge/.test(navigator.userAgent)) {
window.open('', '_self', ''); // HACK: Not allowed on all browsers
window.close();
}
if (navigator) {
if (navigator.app)
navigator.app.exitApp();
else if (navigator.device)
navigator.device.exitApp();
}
}, constant.timerBeforeClose);
}
return util;
});