Telegram Web, preconfigured for usage in I2P.
http://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
952 lines
25 KiB
952 lines
25 KiB
/*! |
|
* Webogram v0.3.6 - messaging web application for MTProto |
|
* https://github.com/zhukov/webogram |
|
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> |
|
* https://github.com/zhukov/webogram/blob/master/LICENSE |
|
*/ |
|
|
|
angular.module('izhukov.utils', []) |
|
|
|
.provider('Storage', function () { |
|
|
|
this.setPrefix = function (newPrefix) { |
|
ConfigStorage.prefix(newPrefix); |
|
}; |
|
|
|
this.$get = ['$q', function ($q) { |
|
var methods = {}; |
|
angular.forEach(['get', 'set', 'remove'], function (methodName) { |
|
methods[methodName] = function () { |
|
var deferred = $q.defer(), |
|
args = Array.prototype.slice.call(arguments); |
|
|
|
args.push(function (result) { |
|
deferred.resolve(result); |
|
}); |
|
ConfigStorage[methodName].apply(ConfigStorage, args); |
|
|
|
return deferred.promise; |
|
}; |
|
}); |
|
return methods; |
|
}]; |
|
|
|
}) |
|
|
|
.service('qSync', function () { |
|
|
|
return { |
|
when: function (result) { |
|
return {then: function (cb) { |
|
return cb(result); |
|
}}; |
|
} |
|
} |
|
|
|
}) |
|
|
|
.service('FileManager', function ($window, $q, $timeout, qSync) { |
|
|
|
$window.URL = $window.URL || $window.webkitURL; |
|
$window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder; |
|
var buggyUnknownBlob = navigator.userAgent.indexOf('Safari') != -1 && |
|
navigator.userAgent.indexOf('Chrome') == -1; |
|
|
|
var blobSupported = true; |
|
|
|
try { |
|
blobConstruct([], ''); |
|
} catch (e) { |
|
blobSupported = false; |
|
} |
|
|
|
function isBlobAvailable () { |
|
return blobSupported; |
|
} |
|
|
|
function fileCopyTo (fromFileEntry, toFileEntry) { |
|
return getFileWriter(toFileEntry).then(function (fileWriter) { |
|
return fileWriteData(fileWriter, fromFileEntry).then(function () { |
|
return fileWriter; |
|
}, function (error) { |
|
return $q.reject(error); |
|
fileWriter.truncate(0); |
|
}); |
|
}); |
|
} |
|
|
|
function fileWriteData(fileWriter, bytes) { |
|
var deferred = $q.defer(); |
|
|
|
fileWriter.onwriteend = function(e) { |
|
deferred.resolve(); |
|
}; |
|
fileWriter.onerror = function (e) { |
|
deferred.reject(e); |
|
}; |
|
|
|
if (bytes.file) { |
|
bytes.file(function (file) { |
|
fileWriter.write(file); |
|
}, function (error) { |
|
deferred.reject(error); |
|
}) |
|
} |
|
else if (bytes instanceof Blob) { // is file bytes |
|
fileWriter.write(bytes); |
|
} |
|
else { |
|
try { |
|
var blob = blobConstruct([bytesToArrayBuffer(bytes)]); |
|
fileWriter.write(blob); |
|
} catch (e) { |
|
deferred.reject(e); |
|
} |
|
} |
|
|
|
return deferred.promise; |
|
} |
|
|
|
function chooseSaveFile (fileName, ext, mimeType) { |
|
if (!$window.chrome || !chrome.fileSystem || !chrome.fileSystem.chooseEntry) { |
|
return $q.reject(); |
|
}; |
|
var deferred = $q.defer(); |
|
|
|
chrome.fileSystem.chooseEntry({ |
|
type: 'saveFile', |
|
suggestedName: fileName, |
|
accepts: [{ |
|
mimeTypes: [mimeType], |
|
extensions: [ext] |
|
}] |
|
}, function (writableFileEntry) { |
|
deferred.resolve(writableFileEntry); |
|
}); |
|
|
|
return deferred.promise; |
|
} |
|
|
|
function getFileWriter (fileEntry) { |
|
var deferred = $q.defer(); |
|
|
|
fileEntry.createWriter(function (fileWriter) { |
|
deferred.resolve(fileWriter); |
|
}, function (error) { |
|
deferred.reject(error); |
|
}); |
|
|
|
return deferred.promise; |
|
} |
|
|
|
function getFakeFileWriter (mimeType, saveFileCallback) { |
|
var blobParts = [], |
|
fakeFileWriter = { |
|
write: function (blob) { |
|
if (!blobSupported) { |
|
if (fakeFileWriter.onerror) { |
|
fakeFileWriter.onerror(new Error('Blob not supported by browser')); |
|
} |
|
return false; |
|
} |
|
blobParts.push(blob); |
|
setZeroTimeout(function () { |
|
if (fakeFileWriter.onwriteend) { |
|
fakeFileWriter.onwriteend(); |
|
} |
|
}); |
|
}, |
|
truncate: function () { |
|
blobParts = []; |
|
}, |
|
finalize: function () { |
|
var blob = blobConstruct(blobParts, mimeType); |
|
if (saveFileCallback) { |
|
saveFileCallback(blob); |
|
} |
|
return blob; |
|
} |
|
}; |
|
|
|
return fakeFileWriter; |
|
}; |
|
|
|
function getUrl (fileData, mimeType) { |
|
// console.log(dT(), 'get url', fileData, mimeType, fileData.toURL !== undefined, fileData instanceof Blob); |
|
if (fileData.toURL !== undefined) { |
|
return fileData.toURL(mimeType); |
|
} |
|
if (fileData instanceof Blob) { |
|
return URL.createObjectURL(fileData); |
|
} |
|
return 'data:' + mimeType + ';base64,' + bytesToBase64(fileData); |
|
} |
|
|
|
function getDataUrl(blob) { |
|
var deferred; |
|
try { |
|
var reader = new FileReader(); |
|
reader.onloadend = function() { |
|
deferred.resolve(reader.result); |
|
} |
|
reader.readAsDataURL(blob); |
|
} catch (e) { |
|
return $q.reject(e); |
|
} |
|
|
|
deferred = $q.defer(); |
|
|
|
return deferred.promise; |
|
} |
|
|
|
function getFileCorrectUrl(blob, mimeType) { |
|
if (buggyUnknownBlob && blob instanceof Blob) { |
|
var mimeType = blob.type || blob.mimeType || mimeType || ''; |
|
if (!mimeType.match(/image\/(jpeg|gif|png|bmp)|video\/quicktime/)) { |
|
return getDataUrl(blob); |
|
} |
|
} |
|
return qSync.when(getUrl(blob, mimeType)); |
|
} |
|
|
|
function downloadFile (blob, mimeType, fileName) { |
|
if (window.navigator && navigator.msSaveBlob !== undefined) { |
|
window.navigator.msSaveBlob(blob, fileName); |
|
return false; |
|
} |
|
|
|
if (window.navigator && navigator.getDeviceStorage) { |
|
var storageName = 'sdcard'; |
|
switch (mimeType.split('/')[0]) { |
|
case 'video': storageName = 'videos'; break; |
|
case 'audio': storageName = 'music'; break; |
|
case 'image': storageName = 'pictures'; break; |
|
} |
|
var deviceStorage = navigator.getDeviceStorage(storageName); |
|
|
|
var request = deviceStorage.addNamed(blob, fileName); |
|
|
|
request.onsuccess = function () { |
|
console.log('Device storage save result', this.result); |
|
}; |
|
request.onerror = function () { |
|
}; |
|
return; |
|
} |
|
|
|
getFileCorrectUrl(blob, mimeType).then(function (url) { |
|
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); |
|
anchor.href = url; |
|
anchor.target = '_blank'; |
|
anchor.download = fileName; |
|
if (anchor.dataset) { |
|
anchor.dataset.downloadurl = ["video/quicktime", fileName, url].join(':'); |
|
} |
|
$(anchor).css({position: 'absolute', top: 1, left: 1}).appendTo('body'); |
|
|
|
try { |
|
var clickEvent = document.createEvent('MouseEvents'); |
|
clickEvent.initMouseEvent( |
|
'click', true, false, window, 0, 0, 0, 0, 0 |
|
, false, false, false, false, 0, null |
|
); |
|
anchor.dispatchEvent(clickEvent); |
|
} catch (e) { |
|
console.error('Download click error', e); |
|
try { |
|
console.error('Download click error', e); |
|
anchor[0].click(); |
|
} catch (e) { |
|
window.open(url, '_blank'); |
|
} |
|
} |
|
$timeout(function () { |
|
$(anchor).remove(); |
|
}, 100); |
|
}); |
|
} |
|
|
|
return { |
|
isAvailable: isBlobAvailable, |
|
copy: fileCopyTo, |
|
write: fileWriteData, |
|
getFileWriter: getFileWriter, |
|
getFakeFileWriter: getFakeFileWriter, |
|
chooseSave: chooseSaveFile, |
|
getUrl: getUrl, |
|
getDataUrl: getDataUrl, |
|
getFileCorrectUrl: getFileCorrectUrl, |
|
download: downloadFile |
|
}; |
|
}) |
|
|
|
.service('IdbFileStorage', function ($q, $window, FileManager) { |
|
|
|
$window.indexedDB = $window.indexedDB || $window.webkitIndexedDB || $window.mozIndexedDB || $window.OIndexedDB || $window.msIndexedDB; |
|
$window.IDBTransaction = $window.IDBTransaction || $window.webkitIDBTransaction || $window.OIDBTransaction || $window.msIDBTransaction; |
|
|
|
var dbName = 'cachedFiles'; |
|
var dbStoreName = 'files'; |
|
var dbVersion = 1; |
|
var openDbPromise; |
|
var storageIsAvailable = $window.indexedDB !== undefined && |
|
$window.IDBTransaction !== undefined && |
|
navigator.userAgent.indexOf('Safari') == -1; |
|
// As of Safari 8.0 IndexedDB is REALLY slow, no point in it |
|
var storeBlobsAvailable = storageIsAvailable || false; |
|
|
|
function isAvailable () { |
|
return storageIsAvailable; |
|
} |
|
|
|
function openDatabase() { |
|
if (openDbPromise) { |
|
return openDbPromise; |
|
} |
|
|
|
try { |
|
var request = indexedDB.open(dbName, dbVersion), |
|
deferred = $q.defer(), |
|
createObjectStore = function (db) { |
|
db.createObjectStore(dbStoreName); |
|
}; |
|
if (!request) { |
|
throw new Exception(); |
|
} |
|
} catch (error) { |
|
storageIsAvailable = false; |
|
return $q.reject(error); |
|
} |
|
|
|
request.onsuccess = function (event) { |
|
db = request.result; |
|
|
|
db.onerror = function (error) { |
|
storageIsAvailable = false; |
|
console.error('Error creating/accessing IndexedDB database', error); |
|
deferred.reject(error); |
|
}; |
|
|
|
// Interim solution for Google Chrome to create an objectStore. Will be deprecated |
|
if (db.setVersion) { |
|
if (db.version != dbVersion) { |
|
db.setVersion(dbVersion).onsuccess = function () { |
|
createObjectStore(db); |
|
deferred.resolve(db); |
|
}; |
|
} |
|
else { |
|
deferred.resolve(db); |
|
} |
|
} |
|
else { |
|
deferred.resolve(db); |
|
} |
|
}; |
|
|
|
request.onerror = function (event) { |
|
storageIsAvailable = false; |
|
console.error('Error creating/accessing IndexedDB database', event); |
|
deferred.reject(event); |
|
} |
|
|
|
request.onupgradeneeded = function (event) { |
|
createObjectStore(event.target.result); |
|
}; |
|
|
|
return openDbPromise = deferred.promise; |
|
}; |
|
|
|
function saveFile (fileName, blob) { |
|
return openDatabase().then(function (db) { |
|
if (!storeBlobsAvailable) { |
|
return saveFileBase64(db, fileName, blob); |
|
} |
|
|
|
try { |
|
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName), |
|
request = objectStore.put(blob, fileName); |
|
} catch (error) { |
|
if (storeBlobsAvailable) { |
|
storeBlobsAvailable = false; |
|
return saveFileBase64(db, fileName, blob); |
|
} |
|
storageIsAvailable = false; |
|
return $q.reject(error); |
|
} |
|
|
|
var deferred = $q.defer(); |
|
|
|
request.onsuccess = function (event) { |
|
deferred.resolve(blob); |
|
}; |
|
|
|
request.onerror = function (error) { |
|
deferred.reject(error); |
|
}; |
|
|
|
return deferred.promise; |
|
}); |
|
}; |
|
|
|
function saveFileBase64(db, fileName, blob) { |
|
try { |
|
var reader = new FileReader(); |
|
reader.readAsDataURL(blob); |
|
} catch (e) { |
|
storageIsAvailable = false; |
|
return $q.reject(); |
|
} |
|
|
|
var deferred = $q.defer(); |
|
|
|
reader.onloadend = function() { |
|
try { |
|
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName), |
|
request = objectStore.put(reader.result, fileName); |
|
} catch (error) { |
|
storageIsAvailable = false; |
|
deferred.reject(error); |
|
return; |
|
}; |
|
request.onsuccess = function (event) { |
|
deferred.resolve(blob); |
|
}; |
|
|
|
request.onerror = function (error) { |
|
deferred.reject(error); |
|
}; |
|
} |
|
|
|
return deferred.promise; |
|
} |
|
|
|
function getFile (fileName) { |
|
return openDatabase().then(function (db) { |
|
var deferred = $q.defer(), |
|
objectStore = db.transaction([dbStoreName], IDBTransaction.READ || 'readonly').objectStore(dbStoreName), |
|
request = objectStore.get(fileName); |
|
|
|
request.onsuccess = function (event) { |
|
var result = event.target.result; |
|
if (result === undefined) { |
|
deferred.reject(); |
|
} else if (typeof result === 'string' && |
|
result.substr(0, 5) === 'data:') { |
|
deferred.resolve(dataUrlToBlob(result)); |
|
} else { |
|
deferred.resolve(result); |
|
} |
|
}; |
|
|
|
request.onerror = function (error) { |
|
deferred.reject(error); |
|
}; |
|
|
|
return deferred.promise; |
|
}); |
|
} |
|
|
|
function getFileWriter (fileName, mimeType) { |
|
var fakeWriter = FileManager.getFakeFileWriter(mimeType, function (blob) { |
|
saveFile(fileName, blob); |
|
}); |
|
return $q.when(fakeWriter); |
|
} |
|
|
|
openDatabase(); |
|
|
|
return { |
|
isAvailable: isAvailable, |
|
saveFile: saveFile, |
|
getFile: getFile, |
|
getFileWriter: getFileWriter |
|
}; |
|
}) |
|
|
|
|
|
.service('TmpfsFileStorage', function ($q, $window, FileManager) { |
|
|
|
$window.requestFileSystem = $window.requestFileSystem || $window.webkitRequestFileSystem; |
|
|
|
var reqFsPromise, |
|
fileSystem, |
|
storageIsAvailable = $window.requestFileSystem !== undefined; |
|
|
|
function requestFS () { |
|
if (reqFsPromise) { |
|
return reqFsPromise; |
|
} |
|
|
|
if (!$window.requestFileSystem) { |
|
return reqFsPromise = $q.reject({type: 'FS_BROWSER_UNSUPPORTED', description: 'requestFileSystem not present'}); |
|
} |
|
|
|
var deferred = $q.defer(); |
|
|
|
$window.requestFileSystem($window.TEMPORARY, 500 * 1024 * 1024, function (fs) { |
|
cachedFs = fs; |
|
deferred.resolve(); |
|
}, function (e) { |
|
storageIsAvailable = false; |
|
deferred.reject(e); |
|
}); |
|
|
|
return reqFsPromise = deferred.promise; |
|
}; |
|
|
|
function isAvailable () { |
|
return storageIsAvailable; |
|
} |
|
|
|
function getFile (fileName, size) { |
|
size = size || 1; |
|
return requestFS().then(function () { |
|
// console.log(dT(), 'get file', fileName); |
|
var deferred = $q.defer(); |
|
cachedFs.root.getFile(fileName, {create: false}, function(fileEntry) { |
|
fileEntry.file(function(file) { |
|
// console.log(dT(), 'aa', file); |
|
if (file.size >= size) { |
|
deferred.resolve(fileEntry); |
|
} else { |
|
deferred.reject(new Error('FILE_NOT_FOUND')); |
|
} |
|
}, function (error) { |
|
console.log(dT(), 'error', error); |
|
deferred.reject(error); |
|
}); |
|
}, function () { |
|
deferred.reject(new Error('FILE_NOT_FOUND')); |
|
}); |
|
return deferred.promise; |
|
}); |
|
} |
|
|
|
function saveFile (fileName, blob) { |
|
return getFileWriter(fileName).then(function (fileWriter) { |
|
return FileManager.write(fileWriter, blob).then(function () { |
|
return fileWriter.finalize(); |
|
}) |
|
}); |
|
} |
|
|
|
function getFileWriter (fileName) { |
|
// console.log(dT(), 'get file writer', fileName); |
|
return requestFS().then(function () { |
|
var deferred = $q.defer(); |
|
cachedFs.root.getFile(fileName, {create: true}, function (fileEntry) { |
|
FileManager.getFileWriter(fileEntry).then(function (fileWriter) { |
|
fileWriter.finalize = function () { |
|
return fileEntry; |
|
} |
|
deferred.resolve(fileWriter); |
|
}, function (error) { |
|
storageIsAvailable = false; |
|
deferred.reject(error); |
|
}); |
|
}, function (error) { |
|
storageIsAvailable = false; |
|
deferred.reject(error); |
|
}); |
|
|
|
return deferred.promise; |
|
}) |
|
} |
|
|
|
requestFS(); |
|
|
|
return { |
|
isAvailable: isAvailable, |
|
saveFile: saveFile, |
|
getFile: getFile, |
|
getFileWriter: getFileWriter |
|
}; |
|
}) |
|
|
|
.service('MemoryFileStorage', function ($q, FileManager) { |
|
|
|
var storage = {}; |
|
|
|
function isAvailable () { |
|
return true; |
|
} |
|
|
|
function getFile (fileName, size) { |
|
if (storage[fileName]) { |
|
return $q.when(storage[fileName]); |
|
} |
|
return $q.reject(new Error('FILE_NOT_FOUND')); |
|
} |
|
|
|
function saveFile (fileName, blob) { |
|
return $q.when(storage[fileName] = blob); |
|
} |
|
|
|
function getFileWriter (fileName, mimeType) { |
|
var fakeWriter = FileManager.getFakeFileWriter(mimeType, function (blob) { |
|
saveFile(fileName, blob); |
|
}); |
|
return $q.when(fakeWriter); |
|
} |
|
|
|
return { |
|
isAvailable: isAvailable, |
|
saveFile: saveFile, |
|
getFile: getFile, |
|
getFileWriter: getFileWriter |
|
}; |
|
}) |
|
|
|
.service('CryptoWorker', function ($timeout, $q) { |
|
|
|
var webWorker = false, |
|
naClEmbed = false, |
|
taskID = 0, |
|
awaiting = {}, |
|
webCrypto = Config.Modes.webcrypto && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle)/* || window.msCrypto && window.msCrypto.subtle*/, |
|
useSha1Crypto = webCrypto && webCrypto.digest !== undefined, |
|
finalizeTask = function (taskID, result) { |
|
var deferred = awaiting[taskID]; |
|
if (deferred !== undefined) { |
|
// console.log(dT(), 'CW done'); |
|
deferred.resolve(result); |
|
delete awaiting[taskID]; |
|
} |
|
}; |
|
|
|
if (Config.Modes.nacl && |
|
navigator.mimeTypes && |
|
navigator.mimeTypes['application/x-pnacl'] !== undefined) { |
|
var listener = $('<div id="nacl_listener"><embed id="mtproto_crypto" width="0" height="0" src="nacl/mtproto_crypto.nmf" type="application/x-pnacl" /></div>').appendTo($('body'))[0]; |
|
listener.addEventListener('load', function (e) { |
|
naClEmbed = listener.firstChild; |
|
console.log(dT(), 'NaCl ready'); |
|
}, true); |
|
listener.addEventListener('message', function (e) { |
|
finalizeTask(e.data.taskID, e.data.result); |
|
}, true); |
|
listener.addEventListener('error', function (e) { |
|
console.error('NaCl error', e); |
|
}, true); |
|
} |
|
|
|
if (window.Worker) { |
|
var tmpWorker = new Worker('js/lib/crypto_worker.js'); |
|
tmpWorker.onmessage = function (e) { |
|
if (!webWorker) { |
|
webWorker = tmpWorker; |
|
} else { |
|
finalizeTask(e.data.taskID, e.data.result); |
|
} |
|
}; |
|
tmpWorker.onerror = function(error) { |
|
console.error('CW error', error, error.stack); |
|
webWorker = false; |
|
}; |
|
} |
|
|
|
function performTaskWorker (task, params, embed) { |
|
// console.log(dT(), 'CW start', task); |
|
var deferred = $q.defer(); |
|
|
|
awaiting[taskID] = deferred; |
|
|
|
params.task = task; |
|
params.taskID = taskID; |
|
(embed || webWorker).postMessage(params); |
|
|
|
taskID++; |
|
|
|
return deferred.promise; |
|
} |
|
|
|
return { |
|
sha1Hash: function (bytes) { |
|
if (useSha1Crypto) { |
|
// We don't use buffer since typedArray.subarray(...).buffer gives the whole buffer and not sliced one. webCrypto.digest supports typed array |
|
var deferred = $q.defer(), |
|
bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes; |
|
// console.log(dT(), 'Native sha1 start'); |
|
webCrypto.digest({name: 'SHA-1'}, bytesTyped).then(function (digest) { |
|
// console.log(dT(), 'Native sha1 done'); |
|
deferred.resolve(digest); |
|
}, function (e) { |
|
console.error('Crypto digest error', e); |
|
useSha1Crypto = false; |
|
deferred.resolve(sha1HashSync(bytes)); |
|
}); |
|
|
|
return deferred.promise; |
|
} |
|
return $timeout(function () { |
|
return sha1HashSync(bytes); |
|
}); |
|
}, |
|
aesEncrypt: function (bytes, keyBytes, ivBytes) { |
|
if (naClEmbed) { |
|
return performTaskWorker('aes-encrypt', { |
|
bytes: addPadding(convertToArrayBuffer(bytes)), |
|
keyBytes: convertToArrayBuffer(keyBytes), |
|
ivBytes: convertToArrayBuffer(ivBytes) |
|
}, naClEmbed); |
|
} |
|
return $timeout(function () { |
|
return convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes)); |
|
}); |
|
}, |
|
aesDecrypt: function (encryptedBytes, keyBytes, ivBytes) { |
|
if (naClEmbed) { |
|
return performTaskWorker('aes-decrypt', { |
|
encryptedBytes: addPadding(convertToArrayBuffer(encryptedBytes)), |
|
keyBytes: convertToArrayBuffer(keyBytes), |
|
ivBytes: convertToArrayBuffer(ivBytes) |
|
}, naClEmbed); |
|
} |
|
return $timeout(function () { |
|
return convertToArrayBuffer(aesDecryptSync(encryptedBytes, keyBytes, ivBytes)); |
|
}); |
|
}, |
|
factorize: function (bytes) { |
|
bytes = convertToByteArray(bytes); |
|
if (naClEmbed && bytes.length <= 8) { |
|
return performTaskWorker('factorize', {bytes: bytes}, naClEmbed); |
|
} |
|
if (webWorker) { |
|
return performTaskWorker('factorize', {bytes: bytes}); |
|
} |
|
return $timeout(function () { |
|
return pqPrimeFactorization(bytes); |
|
}); |
|
}, |
|
modPow: function (x, y, m) { |
|
if (webWorker) { |
|
return performTaskWorker('mod-pow', { |
|
x: x, |
|
y: y, |
|
m: m |
|
}); |
|
} |
|
return $timeout(function () { |
|
return bytesModPow(x, y, m); |
|
}); |
|
}, |
|
}; |
|
}) |
|
|
|
.service('SearchIndexManager', function () { |
|
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, |
|
trimRe = /^\s+|\s$/g, |
|
accentsReplace = { |
|
a: /[åáâäà]/g, |
|
e: /[éêëè]/g, |
|
i: /[íîïì]/g, |
|
o: /[óôöò]/g, |
|
u: /[úûüù]/g, |
|
c: /ç/g, |
|
ss: /ß/g |
|
} |
|
|
|
return { |
|
createIndex: createIndex, |
|
indexObject: indexObject, |
|
cleanSearchText: cleanSearchText, |
|
search: search |
|
}; |
|
|
|
function createIndex () { |
|
return { |
|
shortIndexes: {}, |
|
fullTexts: {} |
|
} |
|
} |
|
|
|
function cleanSearchText (text) { |
|
text = text.replace(badCharsRe, ' ').replace(trimRe, '').toLowerCase(); |
|
|
|
for (var key in accentsReplace) { |
|
if (accentsReplace.hasOwnProperty(key)) { |
|
text = text.replace(accentsReplace[key], key); |
|
} |
|
} |
|
|
|
return text; |
|
} |
|
|
|
function indexObject (id, searchText, searchIndex) { |
|
if (searchIndex.fullTexts[id] !== undefined) { |
|
return false; |
|
} |
|
|
|
searchText = cleanSearchText(searchText); |
|
|
|
if (!searchText.length) { |
|
return false; |
|
} |
|
|
|
var shortIndexes = searchIndex.shortIndexes; |
|
|
|
searchIndex.fullTexts[id] = searchText; |
|
|
|
angular.forEach(searchText.split(' '), function(searchWord) { |
|
var len = Math.min(searchWord.length, 3), |
|
wordPart, i; |
|
for (i = 1; i <= len; i++) { |
|
wordPart = searchWord.substr(0, i); |
|
if (shortIndexes[wordPart] === undefined) { |
|
shortIndexes[wordPart] = [id]; |
|
} else { |
|
shortIndexes[wordPart].push(id); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function search (query, searchIndex) { |
|
var shortIndexes = searchIndex.shortIndexes, |
|
fullTexts = searchIndex.fullTexts; |
|
|
|
query = cleanSearchText(query); |
|
|
|
var queryWords = query.split(' '), |
|
foundObjs = false, |
|
newFoundObjs, i, j, searchText, found; |
|
|
|
for (i = 0; i < queryWords.length; i++) { |
|
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; |
|
if (!newFoundObjs) { |
|
foundObjs = []; |
|
break; |
|
} |
|
if (foundObjs === false || foundObjs.length > newFoundObjs.length) { |
|
foundObjs = newFoundObjs; |
|
} |
|
} |
|
|
|
newFoundObjs = {}; |
|
|
|
for (j = 0; j < foundObjs.length; j++) { |
|
found = true; |
|
searchText = fullTexts[foundObjs[j]]; |
|
for (i = 0; i < queryWords.length; i++) { |
|
if (searchText.indexOf(queryWords[i]) == -1) { |
|
found = false; |
|
break; |
|
} |
|
} |
|
if (found) { |
|
newFoundObjs[foundObjs[j]] = true; |
|
} |
|
} |
|
|
|
return newFoundObjs; |
|
} |
|
}) |
|
|
|
.service('ExternalResourcesManager', function ($q, $http) { |
|
var urlPromises = {}; |
|
|
|
function downloadImage (url) { |
|
if (urlPromises[url] !== undefined) { |
|
return urlPromises[url]; |
|
} |
|
|
|
return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null}) |
|
.then(function (response) { |
|
window.URL = window.URL || window.webkitURL; |
|
return window.URL.createObjectURL(response.data); |
|
}); |
|
} |
|
|
|
return { |
|
downloadImage: downloadImage |
|
} |
|
}) |
|
|
|
.service('IdleManager', function ($rootScope, $window, $timeout) { |
|
|
|
$rootScope.idle = {isIDLE: false}; |
|
|
|
var toPromise, started = false; |
|
|
|
return { |
|
start: start |
|
}; |
|
|
|
function start () { |
|
if (!started) { |
|
started = true; |
|
$($window).on('blur focus keydown mousedown touchstart', onEvent); |
|
|
|
setTimeout(function () { |
|
onEvent({type: 'blur'}); |
|
}, 0); |
|
} |
|
} |
|
|
|
function onEvent (e) { |
|
// console.log('event', e.type); |
|
if (e.type == 'mousemove') { |
|
$($window).off('mousemove', onEvent); |
|
} |
|
var isIDLE = e.type == 'blur' || e.type == 'timeout' ? true : false; |
|
|
|
$timeout.cancel(toPromise); |
|
if (!isIDLE) { |
|
// console.log('update timeout'); |
|
toPromise = $timeout(function () { |
|
onEvent({type: 'timeout'}); |
|
}, 30000); |
|
} |
|
|
|
if ($rootScope.idle.isIDLE == isIDLE) { |
|
return; |
|
} |
|
|
|
// console.log('IDLE changed', isIDLE); |
|
$rootScope.$apply(function () { |
|
$rootScope.idle.isIDLE = isIDLE; |
|
}); |
|
|
|
if (isIDLE && e.type == 'timeout') { |
|
$($window).on('mousemove', onEvent); |
|
} |
|
} |
|
}) |
|
|
|
.service('AppRuntimeManager', function ($window) { |
|
|
|
return { |
|
reload: function () { |
|
try { |
|
location.reload(); |
|
} catch (e) {}; |
|
|
|
if ($window.chrome && chrome.runtime && chrome.runtime.reload) { |
|
chrome.runtime.reload(); |
|
}; |
|
}, |
|
close: function () { |
|
try { |
|
$window.close(); |
|
} catch (e) {} |
|
}, |
|
focus: function () { |
|
if (window.navigator.mozApps && document.hidden) { |
|
// Get app instance and launch it to bring app to foreground |
|
window.navigator.mozApps.getSelf().onsuccess = function() { |
|
this.result.launch(); |
|
}; |
|
} else { |
|
if (window.chrome && chrome.app && chrome.app.window) { |
|
chrome.app.window.current().focus(); |
|
} |
|
window.focus(); |
|
} |
|
} |
|
} |
|
}) |
|
|
|
|
|
|
|
|
|
|