Created
January 23, 2016 08:47
-
-
Save swarog/3ee380f8ba103965c21c to your computer and use it in GitHub Desktop.
Fix for appcache issue with playing audio in offline mode in the Chrom on Android.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* AppCache audio tag caching fix main code | |
*/ | |
(function () { | |
//document.addEventListener("DOMContentLoaded", function () { | |
hashCode = function (str) { | |
var hash = 0; | |
if (str.length == 0) return hash; | |
for (var i = 0; i < str.length; i++) { | |
char = str.charCodeAt(i); | |
hash = ((hash << 5) - hash) + char; | |
hash = hash & hash; // Convert to 32bit integer | |
} | |
return hash; | |
} | |
mediaObjectFix = function (mediaObjects) { | |
if(typeof globMediaObjects == 'undefined') {globMediaObjects = new Array()}; | |
for (var i = 0; i < mediaObjects.length; i++) { | |
if (!mediaObjects[i].myId) { | |
mediaObjects[i].myId = Math.random(); | |
globMediaObjects.push(mediaObjects[i]);//TODO: remove | |
} | |
(function (mediaObject) { | |
if(mediaObject.src.indexOf('filesystem:') === 0) { | |
return; | |
} | |
var observer = new MutationObserver(function (mutations) { | |
mutations[0].target.play = function(play) { | |
return function() { | |
if(this.src.indexOf('data:') !== 0 && (this.src.indexOf('filesystem:') !== 0 || this.src == 'filesystem://123.mp3')) { | |
var audio = this; | |
//TODO: Replace to real URL | |
this.paused = false; | |
setTimeout(function() { audio.play(); }, 200); | |
return true; | |
} | |
//if(this.paused == false) return; | |
var ret = play.apply(this, arguments); | |
return ret; | |
}; | |
}(mutations[0].target.play); | |
/* mutations[0].target.load = function(load) { | |
return function () { | |
debugger; | |
var ret = load.apply(this, arguments); | |
return ret; | |
} | |
}(mutations[0].target.load);*/ | |
mediaObjectFix(new Array(mutations[0].target)); //TODO: remove array | |
}); | |
var observerConfig = {attributes: true, childList: false, characterData: false, subtree: false, attributeFilter: new Array('src')}; | |
if (!mediaObject.myobserver) { | |
observer.observe(mediaObject, observerConfig); //TODO: seems this handled several times | |
mediaObject.myobserver = observer; | |
} | |
if(mediaObject.src == "" || mediaObject.src.indexOf('data:') === 0 || mediaObject.src == 'about:blank') { | |
return; | |
} | |
/* mediaObject.onplay = function(e) { | |
e.preventDefault(); | |
debugger; | |
return false; | |
}*/ | |
mediaObject.oldSrc = mediaObject.src; | |
mediaObject.myobserver.disconnect(); | |
mediaObject.src = 'filesystem://123.mp3'; | |
mediaObject.myobserver.observe(mediaObject, observerConfig); | |
var xhr = new XMLHttpRequest(); | |
xhr.responseType = 'blob'; | |
xhr.onload = function () { | |
var tmpAnchor = document.createElement('A'); //Dirty hack for URL parsing | |
tmpAnchor.href = mediaObject.oldSrc; | |
var fileArr = tmpAnchor.pathname.split('/'); | |
var fileName = fileArr.pop(); | |
if(fileArr[0] == '') fileArr.shift(1); //TODO: Dirty hack, add checking for different file paths | |
if(fileArr.length > 0) { | |
var createFolder = function(path, folderPathArr) { | |
path = path + '/' + folderPathArr.shift(1); | |
fileSystem.root.getDirectory(path, {create: true, exclusive: false}, function(directoryEntry) { | |
if(folderPathArr.length > 0) { | |
createFolder(path, folderPathArr); | |
} else { | |
directoryEntry.getFile(fileName, {create: true}, function (fileEntry) { | |
var currFileEntry = fileEntry; | |
var currMediaObject = mediaObject; | |
fileEntry.createWriter(function (fileWriter) { | |
fileWriter.write(xhr.response); | |
currMediaObject.myobserver.disconnect(); | |
currMediaObject.src = currFileEntry.toURL(); | |
currMediaObject.myobserver.observe(currMediaObject, observerConfig); | |
currMediaObject.load(); | |
}); | |
}); | |
} | |
}, | |
function () { | |
debugger; | |
}); | |
}; | |
createFolder('', fileArr); | |
} | |
else { | |
fileSystem.root.getFile(fileName, {create: true}, function (fileEntry) { | |
var currFileEntry = fileEntry; | |
var currMediaObject = mediaObject; | |
fileEntry.createWriter(function (fileWriter) { | |
fileWriter.write(xhr.response); | |
currMediaObject.myobserver.disconnect(); | |
currMediaObject.src = currFileEntry.toURL(); | |
currMediaObject.myobserver.observe(currMediaObject, observerConfig); | |
currMediaObject.load(); | |
}); | |
}); | |
} | |
} | |
xhr.open('GET', mediaObject.oldSrc); | |
xhr.send(); | |
}(mediaObjects[i])); | |
} | |
} | |
var observer = new MutationObserver(function (mutations) { | |
var mediaObjectsForFix = new Array(); | |
var domTreeTraversing = function (domNodes) { | |
for (var i = 0; i < domNodes.length; i++) { | |
if (domNodes[i].childNodes.length != 0) { | |
domTreeTraversing(domNodes[i].childNodes); | |
} | |
if (domNodes[i].tagName == "AUDIO") { | |
mediaObjectsForFix.push(domNodes[i]); | |
} | |
} | |
} | |
for (var i = 0; i < mutations.length; i++) { | |
var mutation = mutations[i]; | |
if (mutation.addedNodes.length == 0) { | |
continue; | |
} | |
domTreeTraversing(mutation.addedNodes); | |
} | |
mediaObjectFix(mediaObjectsForFix); | |
}); | |
var observerConfig = {attributes: false, childList: true, characterData: false, subtree: true}; | |
var doFix = function () { | |
document.createElement = function(create) { | |
return function() { | |
var ret = create.apply(this, arguments); | |
if (ret.tagName.toLowerCase() === "audio") { | |
//ret.onloadstart = function() {debugger;} | |
mediaObjectFix(new Array(ret)); //TODO: remove Array | |
} | |
return ret; | |
}; | |
}(document.createElement) | |
observer.observe(document, observerConfig); | |
var audioTags = document.getElementsByTagName('AUDIO'); | |
mediaObjectFix(audioTags); | |
} | |
var fileSystem; | |
//TODO: add storage-space dynamic calculation, and extension | |
window.webkitRequestFileSystem(TEMPORARY, 1024 * 1024 * 300, function (fs) { | |
fileSystem = fs; | |
if (bowser.chrome && bowser.android) { | |
//if (bowser.chrome && bowser.android || true) { | |
window.addEventListener("offline", function (e) { | |
doFix(); | |
}, false); | |
window.addEventListener("online", function (e) { | |
observer.disconnect(); | |
}, false); | |
if (!navigator.onLine) { | |
doFix(); | |
} else { | |
var xhr = new XMLHttpRequest(); | |
xhr.onload = function () { | |
//FIXME: Realise more explicit check for request error | |
if(this.status != 200) { | |
//if(true) { | |
doFix(); | |
} | |
} | |
xhr.open('GET', '/?r=' + Math.random()); | |
xhr.send(); | |
} | |
} | |
}); | |
//}); | |
}()); | |
/*! | |
* Bowser - a browser detector | |
* https://github.com/ded/bowser | |
* MIT License | (c) Dustin Diaz 2015 | |
*/ | |
!function (name, definition) { | |
if (typeof module != 'undefined' && module.exports) module.exports = definition() | |
else if (typeof define == 'function' && define.amd) 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]) || ''; | |
} | |
function getSecondMatch(regex) { | |
var match = ua.match(regex); | |
return (match && match.length > 1 && match[2]) || ''; | |
} | |
var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() | |
, likeAndroid = /like android/i.test(ua) | |
, android = !likeAndroid && /android/i.test(ua) | |
, chromeBook = /CrOS/.test(ua) | |
, edgeVersion = getFirstMatch(/edge\/(\d+(\.\d+)?)/i) | |
, 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 (/yabrowser/i.test(ua)) { | |
result = { | |
name: 'Yandex Browser' | |
, yandexbrowser: t | |
, version: versionIdentifier || getFirstMatch(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i) | |
} | |
} | |
else if (/windows phone/i.test(ua)) { | |
result = { | |
name: 'Windows Phone' | |
, windowsphone: t | |
} | |
if (edgeVersion) { | |
result.msedge = t | |
result.version = edgeVersion | |
} | |
else { | |
result.msie = t | |
result.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 (chromeBook) { | |
result = { | |
name: 'Chrome' | |
, chromeBook: t | |
, chrome: t | |
, version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) | |
} | |
} else if (/chrome.+? edge/i.test(ua)) { | |
result = { | |
name: 'Microsoft Edge' | |
, msedge: t | |
, version: edgeVersion | |
} | |
} | |
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 = { | |
name: getFirstMatch(/^(.*)\/(.*) /), | |
version: getSecondMatch(/^(.*)\/(.*) /) | |
}; | |
} | |
// set webkit or gecko flag for browsers based on these engines | |
if (!result.msedge && /(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 (!result.msedge && (android || result.silk)) { | |
result.android = t | |
} else if (iosdevice) { | |
result[iosdevice] = t | |
result.ios = t | |
} | |
// OS version extraction | |
var osVersion = ''; | |
if (result.windowsphone) { | |
osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); | |
} else 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.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.msedge || | |
(result.msie && result.version >= 10) || | |
(result.yandexbrowser && result.version >= 15) || | |
(result.chrome && result.version >= 20) || | |
(result.firefox && result.version >= 20.0) || | |
(result.safari && result.version >= 6) || | |
(result.opera && result.version >= 10.0) || | |
(result.ios && result.osversion && result.osversion.split(".")[0] >= 6) || | |
(result.blackberry && result.version >= 10.1) | |
) { | |
result.a = t; | |
} | |
else if ((result.msie && result.version < 10) || | |
(result.chrome && result.version < 20) || | |
(result.firefox && result.version < 20.0) || | |
(result.safari && result.version < 6) || | |
(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 : '') | |
bowser.test = function (browserList) { | |
for (var i = 0; i < browserList.length; ++i) { | |
var browserItem = browserList[i]; | |
if (typeof browserItem === 'string') { | |
if (browserItem in bowser) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/* | |
* 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; | |
return bowser | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment