Improved Safari support
Added no-blob IndexedDB support. Disabled for Safari, because IDB is very slow here. Added FileManager.getFileCorrectUrl method for Safari
This commit is contained in:
parent
78525c373d
commit
10c6d37656
@ -1233,17 +1233,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
|||||||
|
|
||||||
if (src.substr(0, 5) == 'data:') {
|
if (src.substr(0, 5) == 'data:') {
|
||||||
remove = true;
|
remove = true;
|
||||||
src = src.substr(5).split(';');
|
var blob = dataUrlToBlob(src);
|
||||||
var contentType = src[0];
|
|
||||||
var base64 = atob(src[1].split(',')[1]);
|
|
||||||
var array = new Uint8Array(base64.length);
|
|
||||||
|
|
||||||
for (var i = 0; i < base64.length; i++) {
|
|
||||||
array[i] = base64.charCodeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
var blob = new Blob([array], {type: contentType});
|
|
||||||
|
|
||||||
ErrorService.confirm({type: 'FILE_CLIPBOARD_PASTE'}).then(function () {
|
ErrorService.confirm({type: 'FILE_CLIPBOARD_PASTE'}).then(function () {
|
||||||
$scope.draftMessage.files = [blob];
|
$scope.draftMessage.files = [blob];
|
||||||
$scope.draftMessage.isMedia = true;
|
$scope.draftMessage.isMedia = true;
|
||||||
|
@ -92,6 +92,52 @@ function uint6ToBase64 (nUint6) {
|
|||||||
: 65;
|
: 65;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function base64ToBlob(base64str, mimeType) {
|
||||||
|
var sliceSize = 1024;
|
||||||
|
var byteCharacters = atob(base64str);
|
||||||
|
var bytesLength = byteCharacters.length;
|
||||||
|
var slicesCount = Math.ceil(bytesLength / sliceSize);
|
||||||
|
var byteArrays = new Array(slicesCount);
|
||||||
|
|
||||||
|
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
||||||
|
var begin = sliceIndex * sliceSize;
|
||||||
|
var end = Math.min(begin + sliceSize, bytesLength);
|
||||||
|
|
||||||
|
var bytes = new Array(end - begin);
|
||||||
|
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
|
||||||
|
bytes[i] = byteCharacters[offset].charCodeAt(0);
|
||||||
|
}
|
||||||
|
byteArrays[sliceIndex] = new Uint8Array(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blobConstruct(byteArrays, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataUrlToBlob(url) {
|
||||||
|
// var name = 'b64blob ' + url.length;
|
||||||
|
// console.time(name);
|
||||||
|
var urlParts = url.split(',');
|
||||||
|
var base64str = urlParts[1];
|
||||||
|
var mimeType = urlParts[0].split(':')[1].split(';')[0];
|
||||||
|
var blob = base64ToBlob(base64str, mimeType);
|
||||||
|
// console.timeEnd(name);
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
function blobConstruct (blobParts, mimeType) {
|
||||||
|
var blob;
|
||||||
|
try {
|
||||||
|
blob = new Blob(blobParts, {type: mimeType});
|
||||||
|
} catch (e) {
|
||||||
|
var bb = new BlobBuilder;
|
||||||
|
angular.forEach(blobParts, function(blobPart) {
|
||||||
|
bb.append(blobPart);
|
||||||
|
});
|
||||||
|
blob = bb.getBlob(mimeType);
|
||||||
|
}
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
function bytesCmp (bytes1, bytes2) {
|
function bytesCmp (bytes1, bytes2) {
|
||||||
var len = bytes1.length;
|
var len = bytes1.length;
|
||||||
if (len != bytes2.length) {
|
if (len != bytes2.length) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
|
angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
|
||||||
|
|
||||||
.factory('MtpApiManager', function (Storage, MtpAuthorizer, MtpNetworkerFactory, MtpSingleInstanceService, ErrorService, $q) {
|
.factory('MtpApiManager', function (Storage, MtpAuthorizer, MtpNetworkerFactory, MtpSingleInstanceService, ErrorService, qSync, $q) {
|
||||||
var cachedNetworkers = {},
|
var cachedNetworkers = {},
|
||||||
cachedUploadNetworkers = {},
|
cachedUploadNetworkers = {},
|
||||||
cachedExportPromise = {},
|
cachedExportPromise = {},
|
||||||
@ -65,9 +65,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cache[dcID] !== undefined) {
|
if (cache[dcID] !== undefined) {
|
||||||
return {then: function (cb) {
|
return qSync.when(cache[dcID]);
|
||||||
cb(cache[dcID]);
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var akk = 'dc' + dcID + '_auth_key',
|
var akk = 'dc' + dcID + '_auth_key',
|
||||||
|
@ -33,10 +33,23 @@ angular.module('izhukov.utils', [])
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.service('FileManager', function ($window, $q, $timeout) {
|
.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.URL = $window.URL || $window.webkitURL;
|
||||||
$window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder;
|
$window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder;
|
||||||
|
var buggyUnknownBlob = navigator.userAgent.indexOf('Safari') != -1;
|
||||||
|
|
||||||
var blobSupported = true;
|
var blobSupported = true;
|
||||||
|
|
||||||
@ -61,20 +74,6 @@ angular.module('izhukov.utils', [])
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function blobConstruct (blobParts, mimeType) {
|
|
||||||
var blob;
|
|
||||||
try {
|
|
||||||
blob = new Blob(blobParts, {type: mimeType});
|
|
||||||
} catch (e) {
|
|
||||||
var bb = new BlobBuilder;
|
|
||||||
angular.forEach(blobParts, function(blobPart) {
|
|
||||||
bb.append(blobPart);
|
|
||||||
});
|
|
||||||
blob = bb.getBlob(mimeType);
|
|
||||||
}
|
|
||||||
return blob;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileWriteData(fileWriter, bytes) {
|
function fileWriteData(fileWriter, bytes) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
@ -182,42 +181,68 @@ angular.module('izhukov.utils', [])
|
|||||||
return 'data:' + mimeType + ';base64,' + bytesToBase64(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) {
|
||||||
|
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) {
|
function downloadFile (blob, mimeType, fileName) {
|
||||||
if (window.navigator && navigator.msSaveBlob !== undefined) {
|
if (window.navigator && navigator.msSaveBlob !== undefined) {
|
||||||
window.navigator.msSaveBlob(blob, fileName);
|
window.navigator.msSaveBlob(blob, fileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||||
var url = getUrl(blob, mimeType);
|
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
|
||||||
|
anchor.href = url;
|
||||||
var anchor = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
|
anchor.target = '_blank';
|
||||||
anchor.href = url;
|
anchor.download = fileName;
|
||||||
anchor.target = '_blank';
|
if (anchor.dataset) {
|
||||||
anchor.download = fileName;
|
anchor.dataset.downloadurl = ["video/quicktime", fileName, url].join(':');
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
}
|
$(anchor).css({position: 'absolute', top: 1, left: 1}).appendTo('body');
|
||||||
$timeout(function () {
|
|
||||||
$(anchor).remove();
|
try {
|
||||||
}, 100);
|
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 {
|
return {
|
||||||
@ -228,6 +253,8 @@ angular.module('izhukov.utils', [])
|
|||||||
getFakeFileWriter: getFakeFileWriter,
|
getFakeFileWriter: getFakeFileWriter,
|
||||||
chooseSave: chooseSaveFile,
|
chooseSave: chooseSaveFile,
|
||||||
getUrl: getUrl,
|
getUrl: getUrl,
|
||||||
|
getDataUrl: getDataUrl,
|
||||||
|
getFileCorrectUrl: getFileCorrectUrl,
|
||||||
download: downloadFile
|
download: downloadFile
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -237,11 +264,15 @@ angular.module('izhukov.utils', [])
|
|||||||
$window.indexedDB = $window.indexedDB || $window.webkitIndexedDB || $window.mozIndexedDB || $window.OIndexedDB || $window.msIndexedDB;
|
$window.indexedDB = $window.indexedDB || $window.webkitIndexedDB || $window.mozIndexedDB || $window.OIndexedDB || $window.msIndexedDB;
|
||||||
$window.IDBTransaction = $window.IDBTransaction || $window.webkitIDBTransaction || $window.OIDBTransaction || $window.msIDBTransaction;
|
$window.IDBTransaction = $window.IDBTransaction || $window.webkitIDBTransaction || $window.OIDBTransaction || $window.msIDBTransaction;
|
||||||
|
|
||||||
var dbName = 'cachedFiles',
|
var dbName = 'cachedFiles';
|
||||||
dbStoreName = 'files',
|
var dbStoreName = 'files';
|
||||||
dbVersion = 1,
|
var dbVersion = 1;
|
||||||
openDbPromise,
|
var openDbPromise;
|
||||||
storageIsAvailable = $window.indexedDB !== undefined && $window.IDBTransaction !== undefined;
|
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 () {
|
function isAvailable () {
|
||||||
return storageIsAvailable;
|
return storageIsAvailable;
|
||||||
@ -307,15 +338,24 @@ angular.module('izhukov.utils', [])
|
|||||||
|
|
||||||
function saveFile (fileName, blob) {
|
function saveFile (fileName, blob) {
|
||||||
return openDatabase().then(function (db) {
|
return openDatabase().then(function (db) {
|
||||||
|
if (!storeBlobsAvailable) {
|
||||||
|
return saveFileBase64(db, fileName, blob);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var deferred = $q.defer(),
|
var objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName),
|
||||||
objectStore = db.transaction([dbStoreName], IDBTransaction.READ_WRITE || 'readwrite').objectStore(dbStoreName),
|
|
||||||
request = objectStore.put(blob, fileName);
|
request = objectStore.put(blob, fileName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (storeBlobsAvailable) {
|
||||||
|
storeBlobsAvailable = false;
|
||||||
|
return saveFileBase64(db, fileName, blob);
|
||||||
|
}
|
||||||
storageIsAvailable = false;
|
storageIsAvailable = false;
|
||||||
return $q.reject(error);
|
return $q.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
request.onsuccess = function (event) {
|
request.onsuccess = function (event) {
|
||||||
deferred.resolve(blob);
|
deferred.resolve(blob);
|
||||||
};
|
};
|
||||||
@ -328,6 +368,38 @@ angular.module('izhukov.utils', [])
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
function getFile (fileName) {
|
||||||
return openDatabase().then(function (db) {
|
return openDatabase().then(function (db) {
|
||||||
var deferred = $q.defer(),
|
var deferred = $q.defer(),
|
||||||
@ -335,10 +407,14 @@ angular.module('izhukov.utils', [])
|
|||||||
request = objectStore.get(fileName);
|
request = objectStore.get(fileName);
|
||||||
|
|
||||||
request.onsuccess = function (event) {
|
request.onsuccess = function (event) {
|
||||||
if (event.target.result === undefined) {
|
var result = event.target.result;
|
||||||
|
if (result === undefined) {
|
||||||
deferred.reject();
|
deferred.reject();
|
||||||
|
} else if (typeof result === 'string' &&
|
||||||
|
result.substr(0, 5) === 'data:') {
|
||||||
|
deferred.resolve(dataUrlToBlob(result));
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(event.target.result);
|
deferred.resolve(result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2500,9 +2500,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
|||||||
});
|
});
|
||||||
|
|
||||||
downloadPromise.then(function (blob) {
|
downloadPromise.then(function (blob) {
|
||||||
var url = FileManager.getUrl(blob, mimeType);
|
FileManager.getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||||
|
historyVideo.url = $sce.trustAsResourceUrl(url);
|
||||||
|
});
|
||||||
|
|
||||||
delete historyVideo.progress;
|
delete historyVideo.progress;
|
||||||
historyVideo.url = $sce.trustAsResourceUrl(url);
|
|
||||||
historyVideo.downloaded = true;
|
historyVideo.downloaded = true;
|
||||||
console.log('video save done');
|
console.log('video save done');
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
@ -2655,9 +2657,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
|||||||
});
|
});
|
||||||
|
|
||||||
downloadPromise.then(function (blob) {
|
downloadPromise.then(function (blob) {
|
||||||
var url = FileManager.getUrl(blob, doc.mime_type);
|
FileManager.getFileCorrectUrl(blob, doc.mime_type).then(function (url) {
|
||||||
|
historyDoc.url = $sce.trustAsResourceUrl(url);
|
||||||
|
})
|
||||||
delete historyDoc.progress;
|
delete historyDoc.progress;
|
||||||
historyDoc.url = $sce.trustAsResourceUrl(url);
|
|
||||||
historyDoc.downloaded = true;
|
historyDoc.downloaded = true;
|
||||||
console.log('file save done');
|
console.log('file save done');
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
@ -2771,9 +2774,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
|||||||
});
|
});
|
||||||
|
|
||||||
downloadPromise.then(function (blob) {
|
downloadPromise.then(function (blob) {
|
||||||
var url = FileManager.getUrl(blob, mimeType);
|
FileManager.getFileCorrectUrl(blob, mimeType).then(function (url) {
|
||||||
|
historyAudio.url = $sce.trustAsResourceUrl(url);
|
||||||
|
});
|
||||||
delete historyAudio.progress;
|
delete historyAudio.progress;
|
||||||
historyAudio.url = $sce.trustAsResourceUrl(url);
|
|
||||||
historyAudio.downloaded = true;
|
historyAudio.downloaded = true;
|
||||||
console.log('audio save done');
|
console.log('audio save done');
|
||||||
}, function (e) {
|
}, function (e) {
|
||||||
|
Loading…
Reference in New Issue
Block a user