diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d9ce658a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @aferditamuriqi diff --git a/build.gradle b/build.gradle index 7d71d5c7..e7abc85e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.31' - ext.r2branch = 'develop' + ext.kotlin_version = '1.3.61' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 594dceb0..29f13391 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/r2-navigator/build.gradle b/r2-navigator/build.gradle index 4810f3db..76fc3bc7 100644 --- a/r2-navigator/build.gradle +++ b/r2-navigator/build.gradle @@ -14,15 +14,16 @@ apply plugin: 'com.github.dcendents.android-maven' group='com.github.readium' android { - flavorDimensions "testapp" - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { minSdkVersion 21 - targetSdkVersion 28 - + targetSdkVersion 29 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - + } + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 } buildTypes { release { @@ -30,23 +31,6 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - productFlavors { - intTestapp { - dimension "testapp" - } - intTestappWithLcp { - dimension "testapp" - } - devTestapp { - dimension "testapp" - } - devTestappWithLcp { - dimension "testapp" - } - } - - // default build flavour - defaultPublishConfig "intTestappDebug" } dependencies { @@ -56,28 +40,28 @@ dependencies { if (findProject(':r2-shared')) { implementation project(':r2-shared') } else { - implementation "com.github.readium:r2-shared-kotlin:1.0.10" + implementation "com.github.readium:r2-shared-kotlin:1.1.6" } - implementation "androidx.appcompat:appcompat:1.1.0-beta01" + implementation "androidx.appcompat:appcompat:1.2.0-alpha01" implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.webkit:webkit:1.0.0' + implementation 'androidx.webkit:webkit:1.1.0' implementation "androidx.legacy:legacy-support-v4:1.0.0" - implementation "com.google.android.material:material:1.1.0-alpha07" - implementation "androidx.recyclerview:recyclerview:1.1.0-alpha06" + implementation "com.google.android.material:material:1.2.0-alpha03" + implementation "androidx.recyclerview:recyclerview:1.1.0" implementation 'joda-time:joda-time:2.9.9' implementation "androidx.legacy:legacy-support-core-ui:1.0.0" implementation 'com.duolingo.open:rtl-viewpager:1.0.3' - implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha05' + implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'com.jakewharton.timber:timber:4.7.1' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1' - implementation 'org.zeroturnaround:zt-zip:1.12' + implementation 'org.zeroturnaround:zt-zip:1.13' - implementation 'org.jsoup:jsoup:1.11.2' + implementation 'org.jsoup:jsoup:1.10.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/r2-navigator/r2-navigator.iml b/r2-navigator/r2-navigator.iml deleted file mode 100644 index 96d610ed..00000000 --- a/r2-navigator/r2-navigator.iml +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/r2-navigator/src/main/AndroidManifest.xml b/r2-navigator/src/main/AndroidManifest.xml index 22f4467c..8031e81e 100644 --- a/r2-navigator/src/main/AndroidManifest.xml +++ b/r2-navigator/src/main/AndroidManifest.xml @@ -21,10 +21,16 @@ tools:replace="android:allowBackup"> + + diff --git a/r2-navigator/src/main/assets/divina/divinaPlayer.html b/r2-navigator/src/main/assets/divina/divinaPlayer.html new file mode 100755 index 00000000..9e2cdf9d --- /dev/null +++ b/r2-navigator/src/main/assets/divina/divinaPlayer.html @@ -0,0 +1,27 @@ + + + + + DiViNa Player + + + + + + + + \ No newline at end of file diff --git a/r2-navigator/src/main/assets/divina/divinaPlayer.js b/r2-navigator/src/main/assets/divina/divinaPlayer.js new file mode 100644 index 00000000..5ba425f3 --- /dev/null +++ b/r2-navigator/src/main/assets/divina/divinaPlayer.js @@ -0,0 +1,271 @@ +window.divinaPlayer=function(t){var e={};function i(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,i),n.l=!0,n.exports}return i.m=t,i.c=e,i.d=function(t,e,r){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)i.d(r,n,function(e){return t[e]}.bind(null,n));return r},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=20)}([function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=function(){function t(t,e){for(var i=0;i",'"',"`"," ","\r","\n","\t"]),l=["'"].concat(u),c=["%","/","?",";","#"].concat(l),d=["/","?","#"],p=/^[+a-z0-9A-Z_-]{0,63}$/,f=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,g={javascript:!0,"javascript:":!0},v={javascript:!0,"javascript:":!0},y={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},_=i(17);function m(t,e,i){if(t&&n.isObject(t)&&t instanceof o)return t;var r=new o;return r.parse(t,e,i),r}o.prototype.parse=function(t,e,i){if(!n.isString(t))throw new TypeError("Parameter 'url' must be a string, not "+typeof t);var o=t.indexOf("?"),a=-1!==o&&o127?R+="x":R+=D[k];if(!R.match(p)){var L=I.slice(0,A),N=I.slice(A+1),B=D.match(f);B&&(L.push(B[1]),N.unshift(B[2])),N.length&&(m="/"+N.join(".")+m),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),O||(this.hostname=r.toASCII(this.hostname));var U=this.port?":"+this.port:"",j=this.hostname||"";this.host=j+U,this.href+=this.host,O&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==m[0]&&(m="/"+m))}if(!g[w])for(A=0,M=l.length;A0)&&i.host.split("@"))&&(i.auth=O.shift(),i.host=i.hostname=O.shift());return i.search=t.search,i.query=t.query,n.isNull(i.pathname)&&n.isNull(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.href=i.format(),i}if(!T.length)return i.pathname=null,i.search?i.path="/"+i.search:i.path=null,i.href=i.format(),i;for(var S=T.slice(-1)[0],P=(i.host||t.host||T.length>1)&&("."===S||".."===S)||""===S,A=0,C=T.length;C>=0;C--)"."===(S=T[C])?T.splice(C,1):".."===S?(T.splice(C,1),A++):A&&(T.splice(C,1),A--);if(!b&&!w)for(;A--;A)T.unshift("..");!b||""===T[0]||T[0]&&"/"===T[0].charAt(0)||T.unshift(""),P&&"/"!==T.join("/").substr(-1)&&T.push("");var O,I=""===T[0]||T[0]&&"/"===T[0].charAt(0);E&&(i.hostname=i.host=I?"":T.length?T.shift():"",(O=!!(i.host&&i.host.indexOf("@")>0)&&i.host.split("@"))&&(i.auth=O.shift(),i.host=i.hostname=O.shift()));return(b=b||i.host&&T.length)&&!I&&T.unshift(""),T.length?i.pathname=T.join("/"):(i.pathname=null,i.path=null),n.isNull(i.pathname)&&n.isNull(i.search)||(i.path=(i.pathname?i.pathname:"")+(i.search?i.search:"")),i.auth=t.auth||i.auth,i.slashes=i.slashes||t.slashes,i.href=i.format(),i},o.prototype.parseHost=function(){var t=this.host,e=a.exec(t);e&&(":"!==(e=e[0])&&(this.port=e.substr(1)),t=t.substr(0,t.length-e.length)),t&&(this.hostname=t)}},function(t,e,i){var r; +/*! Hammer.JS - v2.0.7 - 2016-04-22 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ +/*! Hammer.JS - v2.0.7 - 2016-04-22 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ +!function(n,o,s,a){"use strict";var h,u=["","webkit","Moz","MS","ms","o"],l=o.createElement("div"),c="function",d=Math.round,p=Math.abs,f=Date.now;function g(t,e,i){return setTimeout(w(t,i),e)}function v(t,e,i){return!!Array.isArray(t)&&(y(t,i[e],i),!0)}function y(t,e,i){var r;if(t)if(t.forEach)t.forEach(e,i);else if(t.length!==a)for(r=0;r\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",o=n.console&&(n.console.warn||n.console.log);return o&&o.call(n.console,r,i),t.apply(this,arguments)}}h="function"!=typeof Object.assign?function(t){if(t===a||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),i=1;i-1}function O(t){return t.trim().split(/\s+/g)}function I(t,e,i){if(t.indexOf&&!i)return t.indexOf(e);for(var r=0;ri[e]}):r.sort()),r}function R(t,e){for(var i,r,n=e[0].toUpperCase()+e.slice(1),o=0;o1&&!i.firstMultiple?i.firstMultiple=it(e):1===n&&(i.firstMultiple=!1);var o=i.firstInput,s=i.firstMultiple,h=s?s.center:o.center,u=e.center=rt(r);e.timeStamp=f(),e.deltaTime=e.timeStamp-o.timeStamp,e.angle=at(h,u),e.distance=st(h,u),function(t,e){var i=e.center,r=t.offsetDelta||{},n=t.prevDelta||{},o=t.prevInput||{};e.eventType!==j&&o.eventType!==z||(n=t.prevDelta={x:o.deltaX||0,y:o.deltaY||0},r=t.offsetDelta={x:i.x,y:i.y});e.deltaX=n.x+(i.x-r.x),e.deltaY=n.y+(i.y-r.y)}(i,e),e.offsetDirection=ot(e.deltaX,e.deltaY);var l=nt(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=l.x,e.overallVelocityY=l.y,e.overallVelocity=p(l.x)>p(l.y)?l.x:l.y,e.scale=s?(c=s.pointers,d=r,st(d[0],d[1],$)/st(c[0],c[1],$)):1,e.rotation=s?function(t,e){return at(e[1],e[0],$)+at(t[1],t[0],$)}(s.pointers,r):0,e.maxPointers=i.prevInput?e.pointers.length>i.prevInput.maxPointers?e.pointers.length:i.prevInput.maxPointers:e.pointers.length,function(t,e){var i,r,n,o,s=t.lastInterval||e,h=e.timeStamp-s.timeStamp;if(e.eventType!=X&&(h>U||s.velocity===a)){var u=e.deltaX-s.deltaX,l=e.deltaY-s.deltaY,c=nt(h,u,l);r=c.x,n=c.y,i=p(c.x)>p(c.y)?c.x:c.y,o=ot(u,l),t.lastInterval=e}else i=s.velocity,r=s.velocityX,n=s.velocityY,o=s.direction;e.velocity=i,e.velocityX=r,e.velocityY=n,e.direction=o}(i,e);var c,d;var g=t.element;A(e.srcEvent.target,g)&&(g=e.srcEvent.target);e.target=g}(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function it(t){for(var e=[],i=0;i=p(e)?t<0?Y:V:e<0?W:q}function st(t,e,i){i||(i=Q);var r=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return Math.sqrt(r*r+n*n)}function at(t,e,i){i||(i=Q);var r=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return 180*Math.atan2(n,r)/Math.PI}tt.prototype={handler:function(){},init:function(){this.evEl&&S(this.element,this.evEl,this.domHandler),this.evTarget&&S(this.target,this.evTarget,this.domHandler),this.evWin&&S(F(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&P(this.element,this.evEl,this.domHandler),this.evTarget&&P(this.target,this.evTarget,this.domHandler),this.evWin&&P(F(this.element),this.evWin,this.domHandler)}};var ht={mousedown:j,mousemove:H,mouseup:z},ut="mousedown",lt="mousemove mouseup";function ct(){this.evEl=ut,this.evWin=lt,this.pressed=!1,tt.apply(this,arguments)}b(ct,tt,{handler:function(t){var e=ht[t.type];e&j&&0===t.button&&(this.pressed=!0),e&H&&1!==t.which&&(e=z),this.pressed&&(e&z&&(this.pressed=!1),this.callback(this.manager,e,{pointers:[t],changedPointers:[t],pointerType:"mouse",srcEvent:t}))}});var dt={pointerdown:j,pointermove:H,pointerup:z,pointercancel:X,pointerout:X},pt={2:"touch",3:"pen",4:"mouse",5:"kinect"},ft="pointerdown",gt="pointermove pointerup pointercancel";function vt(){this.evEl=ft,this.evWin=gt,tt.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}n.MSPointerEvent&&!n.PointerEvent&&(ft="MSPointerDown",gt="MSPointerMove MSPointerUp MSPointerCancel"),b(vt,tt,{handler:function(t){var e=this.store,i=!1,r=t.type.toLowerCase().replace("ms",""),n=dt[r],o=pt[t.pointerType]||t.pointerType,s="touch"==o,a=I(e,t.pointerId,"pointerId");n&j&&(0===t.button||s)?a<0&&(e.push(t),a=e.length-1):n&(z|X)&&(i=!0),a<0||(e[a]=t,this.callback(this.manager,n,{pointers:e,changedPointers:[t],pointerType:o,srcEvent:t}),i&&e.splice(a,1))}});var yt={touchstart:j,touchmove:H,touchend:z,touchcancel:X},_t="touchstart",mt="touchstart touchmove touchend touchcancel";function xt(){this.evTarget=_t,this.evWin=mt,this.started=!1,tt.apply(this,arguments)}b(xt,tt,{handler:function(t){var e=yt[t.type];if(e===j&&(this.started=!0),this.started){var i=function(t,e){var i=M(t.touches),r=M(t.changedTouches);e&(z|X)&&(i=D(i.concat(r),"identifier",!0));return[i,r]}.call(this,t,e);e&(z|X)&&i[0].length-i[1].length==0&&(this.started=!1),this.callback(this.manager,e,{pointers:i[0],changedPointers:i[1],pointerType:"touch",srcEvent:t})}}});var bt={touchstart:j,touchmove:H,touchend:z,touchcancel:X},wt="touchstart touchmove touchend touchcancel";function Tt(){this.evTarget=wt,this.targetIds={},tt.apply(this,arguments)}b(Tt,tt,{handler:function(t){var e=bt[t.type],i=function(t,e){var i=M(t.touches),r=this.targetIds;if(e&(j|H)&&1===i.length)return r[i[0].identifier]=!0,[i,i];var n,o,s=M(t.changedTouches),a=[],h=this.target;if(o=i.filter(function(t){return A(t.target,h)}),e===j)for(n=0;n-1&&r.splice(t,1)},Et)}}b(Pt,tt,{handler:function(t,e,i){var r="touch"==i.pointerType,n="mouse"==i.pointerType;if(!(n&&i.sourceCapabilities&&i.sourceCapabilities.firesTouchEvents)){if(r)(function(t,e){t&j?(this.primaryTouch=e.changedPointers[0].identifier,At.call(this,e)):t&(z|X)&&At.call(this,e)}).call(this,e,i);else if(n&&function(t){for(var e=t.srcEvent.clientX,i=t.srcEvent.clientY,r=0;r-1&&this.requireFail.splice(e,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(t){return!!this.simultaneous[t.id]},emit:function(t){var e=this,i=this.state;function r(i){e.manager.emit(i,t)}i=jt&&r(e.options.event+Gt(i))},tryEmit:function(t){if(this.canEmit())return this.emit(t);this.state=32},canEmit:function(){for(var t=0;te.threshold&&n&e.direction},attrTest:function(t){return Wt.prototype.attrTest.call(this,t)&&(this.state&Bt||!(this.state&Bt)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=Yt(t.direction);e&&(t.additionalEvent=this.options.event+e),this._super.emit.call(this,t)}}),b(Jt,Wt,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[Dt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||this.state&Bt)},emit:function(t){if(1!==t.scale){var e=t.scale<1?"in":"out";t.additionalEvent=this.options.event+e}this._super.emit.call(this,t)}}),b(Zt,Xt,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[It]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,r=t.distancee.time;if(this._input=t,!r||!i||t.eventType&(z|X)&&!n)this.reset();else if(t.eventType&j)this.reset(),this._timer=g(function(){this.state=Ht,this.tryEmit()},e.time,this);else if(t.eventType&z)return Ht;return 32},reset:function(){clearTimeout(this._timer)},emit:function(t){this.state===Ht&&(t&&t.eventType&z?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=f(),this.manager.emit(this.options.event,this._input)))}}),b(Kt,Wt,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[Dt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||this.state&Bt)}}),b(Qt,Wt,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:J|Z,pointers:1},getTouchAction:function(){return qt.prototype.getTouchAction.call(this)},attrTest:function(t){var e,i=this.options.direction;return i&(J|Z)?e=t.overallVelocity:i&J?e=t.overallVelocityX:i&Z&&(e=t.overallVelocityY),this._super.attrTest.call(this,t)&&i&t.offsetDirection&&t.distance>this.options.threshold&&t.maxPointers==this.options.pointers&&p(e)>this.options.velocity&&t.eventType&z},emit:function(t){var e=Yt(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),b($t,Xt,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[Mt]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,r=t.distance80*i){r=h=t[0],a=u=t[1];for(var x=i;xh&&(h=l),p>u&&(u=p);g=0!==(g=Math.max(h-r,u-a))?1/g:0}return s(_,m,i,r,a,g),m}function n(t,e,i,r,n){var o,s;if(n===S(t,e,i,r)>0)for(o=e;o=e;o-=r)s=w(o,t[o],t[o+1],s);return s&&_(s,s.next)&&(T(s),s=s.next),s}function o(t,e){if(!t)return t;e||(e=t);var i,r=t;do{if(i=!1,r.steiner||!_(r,r.next)&&0!==y(r.prev,r,r.next))r=r.next;else{if(T(r),(r=e=r.prev)===r.next)break;i=!0}}while(i||r!==e);return e}function s(t,e,i,r,n,c,d){if(t){!d&&c&&function(t,e,i,r){var n=t;do{null===n.z&&(n.z=p(n.x,n.y,e,i,r)),n.prevZ=n.prev,n.nextZ=n.next,n=n.next}while(n!==t);n.prevZ.nextZ=null,n.prevZ=null,function(t){var e,i,r,n,o,s,a,h,u=1;do{for(i=t,t=null,o=null,s=0;i;){for(s++,r=i,a=0,e=0;e0||h>0&&r;)0!==a&&(0===h||!r||i.z<=r.z)?(n=i,i=i.nextZ,a--):(n=r,r=r.nextZ,h--),o?o.nextZ=n:t=n,n.prevZ=o,o=n;i=r}o.nextZ=null,u*=2}while(s>1)}(n)}(t,r,n,c);for(var f,g,v=t;t.prev!==t.next;)if(f=t.prev,g=t.next,c?h(t,r,n,c):a(t))e.push(f.i/i),e.push(t.i/i),e.push(g.i/i),T(t),t=g.next,v=g.next;else if((t=g)===v){d?1===d?s(t=u(t,e,i),e,i,r,n,c,2):2===d&&l(t,e,i,r,n,c):s(o(t),e,i,r,n,c,1);break}}}function a(t){var e=t.prev,i=t,r=t.next;if(y(e,i,r)>=0)return!1;for(var n=t.next.next;n!==t.prev;){if(g(e.x,e.y,i.x,i.y,r.x,r.y,n.x,n.y)&&y(n.prev,n,n.next)>=0)return!1;n=n.next}return!0}function h(t,e,i,r){var n=t.prev,o=t,s=t.next;if(y(n,o,s)>=0)return!1;for(var a=n.xo.x?n.x>s.x?n.x:s.x:o.x>s.x?o.x:s.x,l=n.y>o.y?n.y>s.y?n.y:s.y:o.y>s.y?o.y:s.y,c=p(a,h,e,i,r),d=p(u,l,e,i,r),f=t.prevZ,v=t.nextZ;f&&f.z>=c&&v&&v.z<=d;){if(f!==t.prev&&f!==t.next&&g(n.x,n.y,o.x,o.y,s.x,s.y,f.x,f.y)&&y(f.prev,f,f.next)>=0)return!1;if(f=f.prevZ,v!==t.prev&&v!==t.next&&g(n.x,n.y,o.x,o.y,s.x,s.y,v.x,v.y)&&y(v.prev,v,v.next)>=0)return!1;v=v.nextZ}for(;f&&f.z>=c;){if(f!==t.prev&&f!==t.next&&g(n.x,n.y,o.x,o.y,s.x,s.y,f.x,f.y)&&y(f.prev,f,f.next)>=0)return!1;f=f.prevZ}for(;v&&v.z<=d;){if(v!==t.prev&&v!==t.next&&g(n.x,n.y,o.x,o.y,s.x,s.y,v.x,v.y)&&y(v.prev,v,v.next)>=0)return!1;v=v.nextZ}return!0}function u(t,e,i){var r=t;do{var n=r.prev,o=r.next.next;!_(n,o)&&m(n,r,r.next,o)&&x(n,o)&&x(o,n)&&(e.push(n.i/i),e.push(r.i/i),e.push(o.i/i),T(r),T(r.next),r=t=o),r=r.next}while(r!==t);return r}function l(t,e,i,r,n,a){var h=t;do{for(var u=h.next.next;u!==h.prev;){if(h.i!==u.i&&v(h,u)){var l=b(h,u);return h=o(h,h.next),l=o(l,l.next),s(h,e,i,r,n,a),void s(l,e,i,r,n,a)}u=u.next}h=h.next}while(h!==t)}function c(t,e){return t.x-e.x}function d(t,e){if(e=function(t,e){var i,r=e,n=t.x,o=t.y,s=-1/0;do{if(o<=r.y&&o>=r.next.y&&r.next.y!==r.y){var a=r.x+(o-r.y)*(r.next.x-r.x)/(r.next.y-r.y);if(a<=n&&a>s){if(s=a,a===n){if(o===r.y)return r;if(o===r.next.y)return r.next}i=r.x=r.x&&r.x>=l&&n!==r.x&&g(oi.x)&&x(r,t)&&(i=r,d=h),r=r.next;return i}(t,e)){var i=b(e,t);o(i,i.next)}}function p(t,e,i,r,n){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-i)*n)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-r)*n)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function f(t){var e=t,i=t;do{(e.x=0&&(t-s)*(r-a)-(i-s)*(e-a)>=0&&(i-s)*(o-a)-(n-s)*(r-a)>=0}function v(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var i=t;do{if(i.i!==t.i&&i.next.i!==t.i&&i.i!==e.i&&i.next.i!==e.i&&m(i,i.next,t,e))return!0;i=i.next}while(i!==t);return!1}(t,e)&&x(t,e)&&x(e,t)&&function(t,e){var i=t,r=!1,n=(t.x+e.x)/2,o=(t.y+e.y)/2;do{i.y>o!=i.next.y>o&&i.next.y!==i.y&&n<(i.next.x-i.x)*(o-i.y)/(i.next.y-i.y)+i.x&&(r=!r),i=i.next}while(i!==t);return r}(t,e)}function y(t,e,i){return(e.y-t.y)*(i.x-e.x)-(e.x-t.x)*(i.y-e.y)}function _(t,e){return t.x===e.x&&t.y===e.y}function m(t,e,i,r){return!!(_(t,e)&&_(i,r)||_(t,r)&&_(i,e))||y(t,e,i)>0!=y(t,e,r)>0&&y(i,r,t)>0!=y(i,r,e)>0}function x(t,e){return y(t.prev,t,t.next)<0?y(t,e,t.next)>=0&&y(t,t.prev,e)>=0:y(t,e,t.prev)<0||y(t,t.next,e)<0}function b(t,e){var i=new E(t.i,t.x,t.y),r=new E(e.i,e.x,e.y),n=t.next,o=e.prev;return t.next=e,e.prev=t,i.next=n,n.prev=i,r.next=i,i.prev=r,o.next=r,r.prev=o,r}function w(t,e,i,r){var n=new E(t,e,i);return r?(n.next=r.next,n.prev=r,r.next.prev=n,r.next=n):(n.prev=n,n.next=n),n}function T(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function E(t,e,i){this.i=t,this.x=e,this.y=i,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function S(t,e,i,r){for(var n=0,o=e,s=i-r;o0&&(r+=t[n-1].length,i.holes.push(r))}return i}},function(t,e,i){(function(r,n){var o;!function(r){var s=r.Promise,a=s&&"resolve"in s&&"reject"in s&&"all"in s&&"race"in s&&function(){var t;return new s(function(e){t=e}),"function"==typeof t}();e?(e.Promise=a?s:A,e.Polyfill=A):void 0===(o=function(){return a?s:A}.call(e,i,e,t))||(t.exports=o);var h="pending",u="sealed",l="fulfilled",c="rejected",d=function(){};function p(t){return"[object Array]"===Object.prototype.toString.call(t)}var f,g=void 0!==n?n:setTimeout,v=[];function y(){for(var t=0;t0?1:-1}),Number.isInteger||(Number.isInteger=function(t){return"number"==typeof t&&isFinite(t)&&Math.floor(t)===t}),window.ArrayBuffer||(window.ArrayBuffer=Array),window.Float32Array||(window.Float32Array=Array),window.Uint32Array||(window.Uint32Array=Array),window.Uint16Array||(window.Uint16Array=Array),window.Uint8Array||(window.Uint8Array=Array),window.Int32Array||(window.Int32Array=Array)}).call(this,i(2))},function(t,e,i){(function(t){var r=void 0!==t&&t||"undefined"!=typeof self&&self||window,n=Function.prototype.apply;function o(t,e){this._id=t,this._clearFn=e}e.setTimeout=function(){return new o(n.call(setTimeout,r,arguments),clearTimeout)},e.setInterval=function(){return new o(n.call(setInterval,r,arguments),clearInterval)},e.clearTimeout=e.clearInterval=function(t){t&&t.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},e.enroll=function(t,e){clearTimeout(t._idleTimeoutId),t._idleTimeout=e},e.unenroll=function(t){clearTimeout(t._idleTimeoutId),t._idleTimeout=-1},e._unrefActive=e.active=function(t){clearTimeout(t._idleTimeoutId);var e=t._idleTimeout;e>=0&&(t._idleTimeoutId=setTimeout(function(){t._onTimeout&&t._onTimeout()},e))},i(12),e.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==t&&t.setImmediate||this&&this.setImmediate,e.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==t&&t.clearImmediate||this&&this.clearImmediate}).call(this,i(2))},function(t,e,i){(function(t,e){!function(t,i){"use strict";if(!t.setImmediate){var r,n,o,s,a,h=1,u={},l=!1,c=t.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(t);d=d&&d.setTimeout?d:t,"[object process]"==={}.toString.call(t.process)?r=function(t){e.nextTick(function(){f(t)})}:!function(){if(t.postMessage&&!t.importScripts){var e=!0,i=t.onmessage;return t.onmessage=function(){e=!1},t.postMessage("","*"),t.onmessage=i,e}}()?t.MessageChannel?((o=new MessageChannel).port1.onmessage=function(t){f(t.data)},r=function(t){o.port2.postMessage(t)}):c&&"onreadystatechange"in c.createElement("script")?(n=c.documentElement,r=function(t){var e=c.createElement("script");e.onreadystatechange=function(){f(t),e.onreadystatechange=null,n.removeChild(e),e=null},n.appendChild(e)}):r=function(t){setTimeout(f,0,t)}:(s="setImmediate$"+Math.random()+"$",a=function(e){e.source===t&&"string"==typeof e.data&&0===e.data.indexOf(s)&&f(+e.data.slice(s.length))},t.addEventListener?t.addEventListener("message",a,!1):t.attachEvent("onmessage",a),r=function(e){t.postMessage(s+e,"*")}),d.setImmediate=function(t){"function"!=typeof t&&(t=new Function(""+t));for(var e=new Array(arguments.length-1),i=0;i1)for(var i=1;i= 0x80 (not a basic code point)","invalid-input":"Invalid input"},b=u-l,w=Math.floor,T=String.fromCharCode;function E(t){throw new RangeError(x[t])}function S(t,e){for(var i=t.length,r=[];i--;)r[i]=e(t[i]);return r}function P(t,e){var i=t.split("@"),r="";return i.length>1&&(r=i[0]+"@",t=i[1]),r+S((t=t.replace(m,".")).split("."),e).join(".")}function A(t){for(var e,i,r=[],n=0,o=t.length;n=55296&&e<=56319&&n65535&&(e+=T((t-=65536)>>>10&1023|55296),t=56320|1023&t),e+=T(t)}).join("")}function O(t,e){return t+22+75*(t<26)-((0!=e)<<5)}function I(t,e,i){var r=0;for(t=i?w(t/p):t>>1,t+=w(t/e);t>b*c>>1;r+=u)t=w(t/b);return w(r+(b+1)*t/(t+d))}function M(t){var e,i,r,n,o,s,a,d,p,y,_,m=[],x=t.length,b=0,T=g,S=f;for((i=t.lastIndexOf(v))<0&&(i=0),r=0;r=128&&E("not-basic"),m.push(t.charCodeAt(r));for(n=i>0?i+1:0;n=x&&E("invalid-input"),((d=(_=t.charCodeAt(n++))-48<10?_-22:_-65<26?_-65:_-97<26?_-97:u)>=u||d>w((h-b)/s))&&E("overflow"),b+=d*s,!(d<(p=a<=S?l:a>=S+c?c:a-S));a+=u)s>w(h/(y=u-p))&&E("overflow"),s*=y;S=I(b-o,e=m.length+1,0==o),w(b/e)>h-T&&E("overflow"),T+=w(b/e),b%=e,m.splice(b++,0,T)}return C(m)}function D(t){var e,i,r,n,o,s,a,d,p,y,_,m,x,b,S,P=[];for(m=(t=A(t)).length,e=g,i=0,o=f,s=0;s=e&&_w((h-i)/(x=r+1))&&E("overflow"),i+=(a-e)*x,e=a,s=0;sh&&E("overflow"),_==e){for(d=i,p=u;!(d<(y=p<=o?l:p>=o+c?c:p-o));p+=u)S=d-y,b=u-y,P.push(T(O(y+S%b,0))),d=w(S/b);P.push(T(O(d,0))),o=I(i,x,r==n),i=0,++r}++i,++e}return P.join("")}a={version:"1.4.1",ucs2:{decode:A,encode:C},decode:M,encode:D,toASCII:function(t){return P(t,function(t){return _.test(t)?"xn--"+D(t):t})},toUnicode:function(t){return P(t,function(t){return y.test(t)?M(t.slice(4).toLowerCase()):t})}},void 0===(n=function(){return a}.call(e,i,e,t))||(t.exports=n)}()}).call(this,i(15)(t),i(2))},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,i){"use strict";t.exports={isString:function(t){return"string"==typeof t},isObject:function(t){return"object"==typeof t&&null!==t},isNull:function(t){return null===t},isNullOrUndefined:function(t){return null==t}}},function(t,e,i){"use strict";e.decode=e.parse=i(18),e.encode=e.stringify=i(19)},function(t,e,i){"use strict";function r(t,e){return Object.prototype.hasOwnProperty.call(t,e)}t.exports=function(t,e,i,o){e=e||"&",i=i||"=";var s={};if("string"!=typeof t||0===t.length)return s;var a=/\+/g;t=t.split(e);var h=1e3;o&&"number"==typeof o.maxKeys&&(h=o.maxKeys);var u=t.length;h>0&&u>h&&(u=h);for(var l=0;l=0?(c=g.substr(0,v),d=g.substr(v+1)):(c=g,d=""),p=decodeURIComponent(c),f=decodeURIComponent(d),r(s,p)?n(s[p])?s[p].push(f):s[p]=[s[p],f]:s[p]=f}return s};var n=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}},function(t,e,i){"use strict";var r=function(t){switch(typeof t){case"string":return t;case"boolean":return t?"true":"false";case"number":return isFinite(t)?t:"";default:return""}};t.exports=function(t,e,i,a){return e=e||"&",i=i||"=",null===t&&(t=void 0),"object"==typeof t?o(s(t),function(s){var a=encodeURIComponent(r(s))+i;return n(t[s])?o(t[s],function(t){return a+encodeURIComponent(r(t))}).join(e):a+encodeURIComponent(r(t[s]))}).join(e):a?encodeURIComponent(r(a))+i+encodeURIComponent(r(t)):""};var n=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)};function o(t,e){if(t.map)return t.map(e);for(var i=[],r=0;r=11&&(e=!0)}if(c.a.android.device){var r=navigator.userAgent.match(/Android\s([0-9.]*)/);r&&parseInt(r[1],10)>=7&&(e=!0)}}return e?t:4}(32),SPRITE_BATCH_SIZE:4096,RENDER_OPTIONS:{view:null,antialias:!1,forceFXAA:!1,autoDensity:!1,transparent:!1,backgroundColor:0,clearBeforeRender:!0,preserveDrawingBuffer:!1,width:800,height:600,legacy:!1},GC_MODE:0,GC_MAX_IDLE:3600,GC_MAX_CHECK_COUNT:600,WRAP_MODE:33071,SCALE_MODE:1,PRECISION_VERTEX:"highp",PRECISION_FRAGMENT:c.a.apple.device?"highp":"mediump",CAN_UPLOAD_SAME_BUFFER:!c.a.apple.device,CREATE_IMAGE_BITMAP:!1,ROUND_PIXELS:!1},p=i(6),f=i.n(p),g=i(7),v=i.n(g),y=i(3),_=i.n(y),m={WEBGL_LEGACY:0,WEBGL:1,WEBGL2:2},x={UNKNOWN:0,WEBGL:1,CANVAS:2},b={NORMAL:0,ADD:1,MULTIPLY:2,SCREEN:3,OVERLAY:4,DARKEN:5,LIGHTEN:6,COLOR_DODGE:7,COLOR_BURN:8,HARD_LIGHT:9,SOFT_LIGHT:10,DIFFERENCE:11,EXCLUSION:12,HUE:13,SATURATION:14,COLOR:15,LUMINOSITY:16,NORMAL_NPM:17,ADD_NPM:18,SCREEN_NPM:19,NONE:20,SRC_OVER:0,SRC_IN:21,SRC_OUT:22,SRC_ATOP:23,DST_OVER:24,DST_IN:25,DST_OUT:26,DST_ATOP:27,ERASE:26,SUBTRACT:28},w={POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6},T={RGBA:6408,RGB:6407,ALPHA:6406,LUMINANCE:6409,LUMINANCE_ALPHA:6410,DEPTH_COMPONENT:6402,DEPTH_STENCIL:34041},E={TEXTURE_2D:3553,TEXTURE_CUBE_MAP:34067,TEXTURE_2D_ARRAY:35866,TEXTURE_CUBE_MAP_POSITIVE_X:34069,TEXTURE_CUBE_MAP_NEGATIVE_X:34070,TEXTURE_CUBE_MAP_POSITIVE_Y:34071,TEXTURE_CUBE_MAP_NEGATIVE_Y:34072,TEXTURE_CUBE_MAP_POSITIVE_Z:34073,TEXTURE_CUBE_MAP_NEGATIVE_Z:34074},S={UNSIGNED_BYTE:5121,UNSIGNED_SHORT:5123,UNSIGNED_SHORT_5_6_5:33635,UNSIGNED_SHORT_4_4_4_4:32819,UNSIGNED_SHORT_5_5_5_1:32820,FLOAT:5126,HALF_FLOAT:36193},P=1,A=0,C=33071,O=10497,I=1,M=1,D={LOW:"lowp",MEDIUM:"mediump",HIGH:"highp"}; +/*! + * @pixi/utils - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/utils is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +d.RETINA_PREFIX=/@([0-9\.]+)x/,d.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT=!0;var R,k=!1,F="5.1.2";function L(){k=!0}function N(t){if(!k){if(navigator.userAgent.toLowerCase().indexOf("chrome")>-1){var e=["\n %c %c %c PixiJS "+F+" - ✰ "+t+" ✰ %c %c http://www.pixijs.com/ %c %c ♥%c♥%c♥ \n\n","background: #ff66a5; padding:5px 0;","background: #ff66a5; padding:5px 0;","color: #ff66a5; background: #030307; padding:5px 0;","background: #ff66a5; padding:5px 0;","background: #ffc3dc; padding:5px 0;","background: #ff66a5; padding:5px 0;","color: #ff2424; background: #fff; padding:5px 0;","color: #ff2424; background: #fff; padding:5px 0;","color: #ff2424; background: #fff; padding:5px 0;"];window.console.log.apply(console,e)}else window.console&&window.console.log("PixiJS "+F+" - "+t+" - http://www.pixijs.com/");k=!0}}function B(){return void 0===R&&(R=function(){var t={stencil:!0,failIfMajorPerformanceCaveat:d.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT};try{if(!window.WebGLRenderingContext)return!1;var e=document.createElement("canvas"),i=e.getContext("webgl",t)||e.getContext("experimental-webgl",t),r=!(!i||!i.getContextAttributes().stencil);if(i){var n=i.getExtension("WEBGL_lose_context");n&&n.loseContext()}return i=null,r}catch(t){return!1}}()),R}function U(t,e){return(e=e||[])[0]=(t>>16&255)/255,e[1]=(t>>8&255)/255,e[2]=(255&t)/255,e}function j(t){return t=t.toString(16),"#"+(t="000000".substr(0,6-t.length)+t)}function H(t){return"string"==typeof t&&"#"===t[0]&&(t=t.substr(1)),parseInt(t,16)}function z(t){return(255*t[0]<<16)+(255*t[1]<<8)+(255*t[2]|0)}var X=function(){for(var t=[],e=[],i=0;i<32;i++)t[i]=i,e[i]=i;t[b.NORMAL_NPM]=b.NORMAL,t[b.ADD_NPM]=b.ADD,t[b.SCREEN_NPM]=b.SCREEN,e[b.NORMAL]=b.NORMAL_NPM,e[b.ADD]=b.ADD_NPM,e[b.SCREEN]=b.SCREEN_NPM;var r=[];return r.push(e),r.push(t),r}();function G(t,e){return X[e?1:0][t]}function Y(t,e,i,r){return i=i||new Float32Array(4),r||void 0===r?(i[0]=t[0]*e,i[1]=t[1]*e,i[2]=t[2]*e):(i[0]=t[0],i[1]=t[1],i[2]=t[2]),i[3]=e,i}function V(t,e){if(1===e)return(255*e<<24)+t;if(0===e)return 0;var i=t>>16&255,r=t>>8&255,n=255&t;return(255*e<<24)+((i=i*e+.5|0)<<16)+((r=r*e+.5|0)<<8)+(n=n*e+.5|0)}function W(t,e,i,r){return(i=i||new Float32Array(4))[0]=(t>>16&255)/255,i[1]=(t>>8&255)/255,i[2]=(255&t)/255,(r||void 0===r)&&(i[0]*=e,i[1]*=e,i[2]*=e),i[3]=e,i}function q(t,e){void 0===e&&(e=null);var i=6*t;if((e=e||new Uint16Array(i)).length!==i)throw new Error("Out buffer length is incorrect, got "+e.length+" and expected "+i);for(var r=0,n=0;r=n||0===i)){var o=n-(i=e+i>n?n-e:i);for(r=e;r>>1,t|=t>>>2,t|=t>>>4,t|=t>>>8,(t|=t>>>16)+1}function tt(t){return!(t&t-1||!t)}function et(t){var e=(t>65535)<<4,i=((t>>>=e)>255)<<3;return e|=i,e|=i=((t>>>=i)>15)<<2,(e|=i=((t>>>=i)>3)<<1)|(t>>>=i)>>1}var it={},rt=Object.create(null),nt=Object.create(null);function ot(){var t;for(t in rt)rt[t].destroy();for(t in nt)nt[t].destroy()}function st(){var t;for(t in rt)delete rt[t];for(t in nt)delete nt[t]}function at(t){var e,i,r,n=t.width,o=t.height,s=t.getContext("2d"),a=s.getImageData(0,0,n,o).data,h=a.length,u={top:null,left:null,right:null,bottom:null},l=null;for(e=0;e=0?Rt.S:Rt.N:2*Math.abs(e)<=Math.abs(t)?t>0?Rt.E:Rt.W:e>0?t>0?Rt.SE:Rt.SW:t>0?Rt.NE:Rt.NW},matrixAppendRotationInv:function(t,e,i,r){void 0===i&&(i=0),void 0===r&&(r=0);var n=Mt[Rt.inv(e)];n.tx=i,n.ty=r,t.append(n)}},kt=function(){this.worldTransform=new Et,this.localTransform=new Et,this.position=new _t(this.onChange,this,0,0),this.scale=new _t(this.onChange,this,1,1),this.pivot=new _t(this.onChange,this,0,0),this.skew=new _t(this.updateSkew,this,0,0),this._rotation=0,this._cx=1,this._sx=0,this._cy=0,this._sy=1,this._localID=0,this._currentLocalID=0,this._worldID=0,this._parentID=0},Ft={rotation:{configurable:!0}};kt.prototype.onChange=function(){this._localID++},kt.prototype.updateSkew=function(){this._cx=Math.cos(this._rotation+this.skew._y),this._sx=Math.sin(this._rotation+this.skew._y),this._cy=-Math.sin(this._rotation-this.skew._x),this._sy=Math.cos(this._rotation-this.skew._x),this._localID++},kt.prototype.updateLocalTransform=function(){var t=this.localTransform;this._localID!==this._currentLocalID&&(t.a=this._cx*this.scale._x,t.b=this._sx*this.scale._x,t.c=this._cy*this.scale._y,t.d=this._sy*this.scale._y,t.tx=this.position._x-(this.pivot._x*t.a+this.pivot._y*t.c),t.ty=this.position._y-(this.pivot._x*t.b+this.pivot._y*t.d),this._currentLocalID=this._localID,this._parentID=-1)},kt.prototype.updateTransform=function(t){var e=this.localTransform;if(this._localID!==this._currentLocalID&&(e.a=this._cx*this.scale._x,e.b=this._sx*this.scale._x,e.c=this._cy*this.scale._y,e.d=this._sy*this.scale._y,e.tx=this.position._x-(this.pivot._x*e.a+this.pivot._y*e.c),e.ty=this.position._y-(this.pivot._x*e.b+this.pivot._y*e.d),this._currentLocalID=this._localID,this._parentID=-1),this._parentID!==t._worldID){var i=t.worldTransform,r=this.worldTransform;r.a=e.a*i.a+e.b*i.c,r.b=e.a*i.b+e.b*i.d,r.c=e.c*i.a+e.d*i.c,r.d=e.c*i.b+e.d*i.d,r.tx=e.tx*i.a+e.ty*i.c+i.tx,r.ty=e.tx*i.b+e.ty*i.d+i.ty,this._parentID=t._worldID,this._worldID++}},kt.prototype.setFromMatrix=function(t){t.decompose(this),this._localID++},Ft.rotation.get=function(){return this._rotation},Ft.rotation.set=function(t){this._rotation!==t&&(this._rotation=t,this.updateSkew())},Object.defineProperties(kt.prototype,Ft),kt.IDENTITY=new kt;var Lt=function(t,e,i,r){void 0===t&&(t=0),void 0===e&&(e=0),void 0===i&&(i=0),void 0===r&&(r=0),this.x=Number(t),this.y=Number(e),this.width=Number(i),this.height=Number(r),this.type=Tt.RECT},Nt={left:{configurable:!0},right:{configurable:!0},top:{configurable:!0},bottom:{configurable:!0}},Bt={EMPTY:{configurable:!0}};Nt.left.get=function(){return this.x},Nt.right.get=function(){return this.x+this.width},Nt.top.get=function(){return this.y},Nt.bottom.get=function(){return this.y+this.height},Bt.EMPTY.get=function(){return new Lt(0,0,0,0)},Lt.prototype.clone=function(){return new Lt(this.x,this.y,this.width,this.height)},Lt.prototype.copyFrom=function(t){return this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height,this},Lt.prototype.copyTo=function(t){return t.x=this.x,t.y=this.y,t.width=this.width,t.height=this.height,t},Lt.prototype.contains=function(t,e){return!(this.width<=0||this.height<=0)&&(t>=this.x&&t=this.y&&ee!=u>e&&t<(e-a)/(u-a)*(h-s)+s&&(i=!i)}return i};var zt=function(t,e,i,r,n){void 0===t&&(t=0),void 0===e&&(e=0),void 0===i&&(i=0),void 0===r&&(r=0),void 0===n&&(n=20),this.x=t,this.y=e,this.width=i,this.height=r,this.radius=n,this.type=Tt.RREC};zt.prototype.clone=function(){return new zt(this.x,this.y,this.width,this.height,this.radius)},zt.prototype.contains=function(t,e){if(this.width<=0||this.height<=0)return!1;if(t>=this.x&&t<=this.x+this.width&&e>=this.y&&e<=this.y+this.height){if(e>=this.y+this.radius&&e<=this.y+this.height-this.radius||t>=this.x+this.radius&&t<=this.x+this.width-this.radius)return!0;var i=t-(this.x+this.radius),r=e-(this.y+this.radius),n=this.radius*this.radius;if(i*i+r*r<=n)return!0;if((i=t-(this.x+this.width-this.radius))*i+r*r<=n)return!0;if(i*i+(r=e-(this.y+this.height-this.radius))*r<=n)return!0;if((i=t-(this.x+this.radius))*i+r*r<=n)return!0}return!1}, +/*! + * @pixi/display - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/display is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +d.SORTABLE_CHILDREN=!1;var Xt=function(){this.minX=1/0,this.minY=1/0,this.maxX=-1/0,this.maxY=-1/0,this.rect=null};Xt.prototype.isEmpty=function(){return this.minX>this.maxX||this.minY>this.maxY},Xt.prototype.clear=function(){this.updateID++,this.minX=1/0,this.minY=1/0,this.maxX=-1/0,this.maxY=-1/0},Xt.prototype.getRectangle=function(t){return this.minX>this.maxX||this.minY>this.maxY?Lt.EMPTY:((t=t||new Lt(0,0,1,1)).x=this.minX,t.y=this.minY,t.width=this.maxX-this.minX,t.height=this.maxY-this.minY,t)},Xt.prototype.addPoint=function(t){this.minX=Math.min(this.minX,t.x),this.maxX=Math.max(this.maxX,t.x),this.minY=Math.min(this.minY,t.y),this.maxY=Math.max(this.maxY,t.y)},Xt.prototype.addQuad=function(t){var e=this.minX,i=this.minY,r=this.maxX,n=this.maxY,o=t[0],s=t[1];e=or?o:r,n=s>n?s:n,e=(o=t[2])r?o:r,n=s>n?s:n,e=(o=t[4])r?o:r,n=s>n?s:n,e=(o=t[6])r?o:r,n=s>n?s:n,this.minX=e,this.minY=i,this.maxX=r,this.maxY=n},Xt.prototype.addFrame=function(t,e,i,r,n){var o=t.worldTransform,s=o.a,a=o.b,h=o.c,u=o.d,l=o.tx,c=o.ty,d=this.minX,p=this.minY,f=this.maxX,g=this.maxY,v=s*e+h*i+l,y=a*e+u*i+c;d=vf?v:f,g=y>g?y:g,d=(v=s*r+h*i+l)f?v:f,g=y>g?y:g,d=(v=s*e+h*n+l)f?v:f,g=y>g?y:g,d=(v=s*r+h*n+l)f?v:f,g=y>g?y:g,this.minX=d,this.minY=p,this.maxX=f,this.maxY=g},Xt.prototype.addVertexData=function(t,e,i){for(var r=this.minX,n=this.minY,o=this.maxX,s=this.maxY,a=e;ao?h:o,s=u>s?u:s}this.minX=r,this.minY=n,this.maxX=o,this.maxY=s},Xt.prototype.addVertices=function(t,e,i,r){for(var n=t.worldTransform,o=n.a,s=n.b,a=n.c,h=n.d,u=n.tx,l=n.ty,c=this.minX,d=this.minY,p=this.maxX,f=this.maxY,g=i;gp?_:p,f=m>f?m:f}this.minX=c,this.minY=d,this.maxX=p,this.maxY=f},Xt.prototype.addBounds=function(t){var e=this.minX,i=this.minY,r=this.maxX,n=this.maxY;this.minX=t.minXr?t.maxX:r,this.maxY=t.maxY>n?t.maxY:n},Xt.prototype.addBoundsMask=function(t,e){var i=t.minX>e.minX?t.minX:e.minX,r=t.minY>e.minY?t.minY:e.minY,n=t.maxXh?n:h,this.maxY=o>u?o:u}},Xt.prototype.addBoundsArea=function(t,e){var i=t.minX>e.x?t.minX:e.x,r=t.minY>e.y?t.minY:e.y,n=t.maxXh?n:h,this.maxY=o>u?o:u}};var Gt=function(t){function e(){t.call(this),this.tempDisplayObjectParent=null,this.transform=new kt,this.alpha=1,this.visible=!0,this.renderable=!0,this.parent=null,this.worldAlpha=1,this._lastSortedIndex=0,this._zIndex=0,this.filterArea=null,this.filters=null,this._enabledFilters=null,this._bounds=new Xt,this._boundsID=0,this._lastBoundsID=-1,this._boundsRect=null,this._localBoundsRect=null,this._mask=null,this._destroyed=!1,this.isSprite=!1}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={_tempDisplayObjectParent:{configurable:!0},x:{configurable:!0},y:{configurable:!0},worldTransform:{configurable:!0},localTransform:{configurable:!0},position:{configurable:!0},scale:{configurable:!0},pivot:{configurable:!0},skew:{configurable:!0},rotation:{configurable:!0},angle:{configurable:!0},zIndex:{configurable:!0},worldVisible:{configurable:!0},mask:{configurable:!0}};return e.mixin=function(t){for(var i=Object.keys(t),r=0;r1)for(var r=0;rthis.children.length)throw new Error(t+"addChildAt: The index "+e+" supplied is out of bounds "+this.children.length);return t.parent&&t.parent.removeChild(t),t.parent=this,this.sortDirty=!0,t.transform._parentID=-1,this.children.splice(e,0,t),this._boundsID++,this.onChildrenChange(e),t.emit("added",this),this.emit("childAdded",t,this,e),t},e.prototype.swapChildren=function(t,e){if(t!==e){var i=this.getChildIndex(t),r=this.getChildIndex(e);this.children[i]=e,this.children[r]=t,this.onChildrenChange(i=this.children.length)throw new Error("The index "+e+" supplied is out of bounds "+this.children.length);var i=this.getChildIndex(t);J(this.children,i,1),this.children.splice(e,0,t),this.onChildrenChange(e)},e.prototype.getChildAt=function(t){if(t<0||t>=this.children.length)throw new Error("getChildAt: Index ("+t+") does not exist.");return this.children[t]},e.prototype.removeChild=function(t){var e=arguments,i=arguments.length;if(i>1)for(var r=0;r0&&o<=n){i=this.children.splice(r,o);for(var s=0;s1&&this.children.sort(Yt),this.sortDirty=!1},e.prototype.updateTransform=function(){this.sortableChildren&&this.sortDirty&&this.sortChildren(),this._boundsID++,this.transform.updateTransform(this.parent.transform),this.worldAlpha=this.alpha*this.parent.worldAlpha;for(var t=0,e=this.children.length;tthis.renderer.width&&(t.width=this.renderer.width-t.x),t.y+t.height>this.renderer.height&&(t.height=this.renderer.height-t.y)},qt.prototype.addChild=function(t){var e=this.pool.pop();e||((e=document.createElement("button")).style.width="100px",e.style.height="100px",e.style.backgroundColor=this.debug?"rgba(255,0,0,0.5)":"transparent",e.style.position="absolute",e.style.zIndex=2,e.style.borderStyle="none",navigator.userAgent.toLowerCase().indexOf("chrome")>-1?e.setAttribute("aria-live","off"):e.setAttribute("aria-live","polite"),navigator.userAgent.match(/rv:.*Gecko\//)?e.setAttribute("aria-relevant","additions"):e.setAttribute("aria-relevant","text"),e.addEventListener("click",this._onClick.bind(this)),e.addEventListener("focus",this._onFocus.bind(this)),e.addEventListener("focusout",this._onFocusOut.bind(this))),t.accessibleTitle&&null!==t.accessibleTitle?e.title=t.accessibleTitle:t.accessibleHint&&null!==t.accessibleHint||(e.title="displayObject "+t.tabIndex),t.accessibleHint&&null!==t.accessibleHint&&e.setAttribute("aria-label",t.accessibleHint),t._accessibleActive=!0,t._accessibleDiv=e,e.displayObject=t,this.children.push(t),this.div.appendChild(t._accessibleDiv),t._accessibleDiv.tabIndex=t.tabIndex},qt.prototype._onClick=function(t){var e=this.renderer.plugins.interaction;e.dispatchEvent(t.target.displayObject,"click",e.eventData),e.dispatchEvent(t.target.displayObject,"pointertap",e.eventData),e.dispatchEvent(t.target.displayObject,"tap",e.eventData)},qt.prototype._onFocus=function(t){t.target.getAttribute("aria-live","off")||t.target.setAttribute("aria-live","assertive");var e=this.renderer.plugins.interaction;e.dispatchEvent(t.target.displayObject,"mouseover",e.eventData)},qt.prototype._onFocusOut=function(t){t.target.getAttribute("aria-live","off")||t.target.setAttribute("aria-live","polite");var e=this.renderer.plugins.interaction;e.dispatchEvent(t.target.displayObject,"mouseout",e.eventData)},qt.prototype._onKeyDown=function(t){9===t.keyCode&&this.activate()},qt.prototype._onMouseMove=function(t){0===t.movementX&&0===t.movementY||this.deactivate()},qt.prototype.destroy=function(){this.destroyTouchHook(),this.div=null;for(var t=0;t8)throw new Error("max arguments reached");var h=this.name,u=this.items;this._aliasCount++;for(var l=0,c=u.length;l0&&this.items.length>1&&(this._aliasCount=0,this.items=this.items.slice(0))},Jt.prototype.add=function(t){return t[this._name]&&(this.ensureNonAliasedItems(),this.remove(t),this.items.push(t)),this},Jt.prototype.remove=function(t){var e=this.items.indexOf(t);return-1!==e&&(this.ensureNonAliasedItems(),this.items.splice(e,1)),this},Jt.prototype.contains=function(t){return-1!==this.items.indexOf(t)},Jt.prototype.removeAll=function(){return this.ensureNonAliasedItems(),this.items.length=0,this},Jt.prototype.destroy=function(){this.removeAll(),this.items=null,this._name=null},Zt.empty.get=function(){return 0===this.items.length},Zt.name.get=function(){return this._name},Object.defineProperties(Jt.prototype,Zt),Jt.prototype.dispatch=Jt.prototype.emit,Jt.prototype.run=Jt.prototype.emit, +/*! + * @pixi/ticker - v5.1.1 + * Compiled Fri, 02 Aug 2019 23:20:23 UTC + * + * @pixi/ticker is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +d.TARGET_FPMS=.06;var Kt=50,Qt=25,$t=0,te=-25,ee=-50,ie=function(t,e,i,r){void 0===e&&(e=null),void 0===i&&(i=0),void 0===r&&(r=!1),this.fn=t,this.context=e,this.priority=i,this.once=r,this.next=null,this.previous=null,this._destroyed=!1};ie.prototype.match=function(t,e){return e=e||null,this.fn===t&&this.context===e},ie.prototype.emit=function(t){this.fn&&(this.context?this.fn.call(this.context,t):this.fn(t));var e=this.next;return this.once&&this.destroy(!0),this._destroyed&&(this.next=null),e},ie.prototype.connect=function(t){this.previous=t,t.next&&(t.next.previous=this),this.next=t.next,t.next=this},ie.prototype.destroy=function(t){void 0===t&&(t=!1),this._destroyed=!0,this.fn=null,this.context=null,this.previous&&(this.previous.next=this.next),this.next&&(this.next.previous=this.previous);var e=this.next;return this.next=t?null:e,this.previous=null,e};var re=function(){var t=this;this._head=new ie(null,null,1/0),this._requestId=null,this._maxElapsedMS=100,this._minElapsedMS=0,this.autoStart=!1,this.deltaTime=1,this.deltaMS=1/d.TARGET_FPMS,this.elapsedMS=1/d.TARGET_FPMS,this.lastTime=-1,this.speed=1,this.started=!1,this._protected=!1,this._lastFrame=-1,this._tick=function(e){t._requestId=null,t.started&&(t.update(e),t.started&&null===t._requestId&&t._head.next&&(t._requestId=requestAnimationFrame(t._tick)))}},ne={FPS:{configurable:!0},minFPS:{configurable:!0},maxFPS:{configurable:!0}},oe={shared:{configurable:!0},system:{configurable:!0}};re.prototype._requestIfNeeded=function(){null===this._requestId&&this._head.next&&(this.lastTime=performance.now(),this._lastFrame=this.lastTime,this._requestId=requestAnimationFrame(this._tick))},re.prototype._cancelIfNeeded=function(){null!==this._requestId&&(cancelAnimationFrame(this._requestId),this._requestId=null)},re.prototype._startIfPossible=function(){this.started?this._requestIfNeeded():this.autoStart&&this.start()},re.prototype.add=function(t,e,i){return void 0===i&&(i=$t),this._addListener(new ie(t,e,i))},re.prototype.addOnce=function(t,e,i){return void 0===i&&(i=$t),this._addListener(new ie(t,e,i,!0))},re.prototype._addListener=function(t){var e=this._head.next,i=this._head;if(e){for(;e;){if(t.priority>e.priority){t.connect(i);break}i=e,e=e.next}t.previous||t.connect(i)}else t.connect(i);return this._startIfPossible(),this},re.prototype.remove=function(t,e){for(var i=this._head.next;i;)i=i.match(t,e)?i.destroy():i.next;return this._head.next||this._cancelIfNeeded(),this},re.prototype.start=function(){this.started||(this.started=!0,this._requestIfNeeded())},re.prototype.stop=function(){this.started&&(this.started=!1,this._cancelIfNeeded())},re.prototype.destroy=function(){if(!this._protected){this.stop();for(var t=this._head.next;t;)t=t.destroy(!0);this._head.destroy(),this._head=null}},re.prototype.update=function(t){var e;if(void 0===t&&(t=performance.now()),t>this.lastTime){if((e=this.elapsedMS=t-this.lastTime)>this._maxElapsedMS&&(e=this._maxElapsedMS),e*=this.speed,this._minElapsedMS){var i=t-this._lastFrame|0;if(i=0;--n){var o=ce[n];if(o.test&&o.test(t,i))return new o(t,e)}return new le(t,e)}var pe=function(t){function e(e,i){var r=i||{},n=r.width,o=r.height;if(!n||!o)throw new Error("BufferResource width or height invalid");t.call(this,n,o),this.data=e}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.upload=function(t,e,i){var r=t.gl;return r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,e.premultiplyAlpha),i.width===e.width&&i.height===e.height?r.texSubImage2D(e.target,0,0,0,e.width,e.height,e.format,e.type,this.data):(i.width=e.width,i.height=e.height,r.texImage2D(e.target,0,i.internalFormat,e.width,e.height,0,e.format,i.type,this.data)),!0},e.prototype.dispose=function(){this.data=null},e.test=function(t){return t instanceof Float32Array||t instanceof Uint8Array||t instanceof Uint32Array},e}(ae),fe={scaleMode:A,format:T.RGBA,premultiplyAlpha:!1},ge=function(t){function e(e,i){void 0===e&&(e=null),void 0===i&&(i=null),t.call(this);var r=(i=i||{}).premultiplyAlpha,n=i.mipmap,o=i.anisotropicLevel,s=i.scaleMode,a=i.width,h=i.height,u=i.wrapMode,l=i.format,c=i.type,p=i.target,f=i.resolution,g=i.resourceOptions;!e||e instanceof ae||((e=de(e,g)).internal=!0),this.width=a||0,this.height=h||0,this.resolution=f||d.RESOLUTION,this.mipmap=void 0!==n?n:d.MIPMAP_TEXTURES,this.anisotropicLevel=void 0!==o?o:d.ANISOTROPIC_LEVEL,this.wrapMode=u||d.WRAP_MODE,this.scaleMode=void 0!==s?s:d.SCALE_MODE,this.format=l||T.RGBA,this.type=c||S.UNSIGNED_BYTE,this.target=p||E.TEXTURE_2D,this.premultiplyAlpha=!1!==r,this.uid=K(),this.touched=0,this.isPowerOfTwo=!1,this._refreshPOT(),this._glTextures={},this.dirtyId=0,this.dirtyStyleId=0,this.cacheId=null,this.valid=a>0&&h>0,this.textureCacheIds=[],this.destroyed=!1,this.resource=null,this._batchEnabled=0,this.setResource(e)}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={realWidth:{configurable:!0},realHeight:{configurable:!0}};return i.realWidth.get=function(){return Math.ceil(this.width*this.resolution-1e-4)},i.realHeight.get=function(){return Math.ceil(this.height*this.resolution-1e-4)},e.prototype.setStyle=function(t,e){var i;return void 0!==t&&t!==this.scaleMode&&(this.scaleMode=t,i=!0),void 0!==e&&e!==this.mipmap&&(this.mipmap=e,i=!0),i&&this.dirtyStyleId++,this},e.prototype.setSize=function(t,e,i){return this.resolution=i||this.resolution,this.width=t,this.height=e,this._refreshPOT(),this.update(),this},e.prototype.setRealSize=function(t,e,i){return this.resolution=i||this.resolution,this.width=t/this.resolution,this.height=e/this.resolution,this._refreshPOT(),this.update(),this},e.prototype._refreshPOT=function(){this.isPowerOfTwo=tt(this.realWidth)&&tt(this.realHeight)},e.prototype.setResolution=function(t){var e=this.resolution;return e===t?this:(this.resolution=t,this.valid&&(this.width=this.width*e/t,this.height=this.height*e/t,this.emit("update",this)),this._refreshPOT(),this)},e.prototype.setResource=function(t){if(this.resource===t)return this;if(this.resource)throw new Error("Resource can be set only once");return t.bind(this),this.resource=t,this},e.prototype.update=function(){this.valid?(this.dirtyId++,this.dirtyStyleId++,this.emit("update",this)):this.width>0&&this.height>0&&(this.valid=!0,this.emit("loaded",this),this.emit("update",this))},e.prototype.onError=function(t){this.emit("error",this,t)},e.prototype.destroy=function(){this.resource&&(this.resource.unbind(this),this.resource.internal&&this.resource.destroy(),this.resource=null),this.cacheId&&(delete nt[this.cacheId],delete rt[this.cacheId],this.cacheId=null),this.dispose(),e.removeFromCache(this),this.textureCacheIds=null,this.destroyed=!0},e.prototype.dispose=function(){this.emit("dispose",this)},e.from=function(t,i){var r=null;"string"==typeof t?r=t:(t._pixiId||(t._pixiId="pixiid_"+K()),r=t._pixiId);var n=nt[r];return n||((n=new e(t,i)).cacheId=r,e.addToCache(n,r)),n},e.fromBuffer=function(t,i,r,n){t=t||new Float32Array(i*r*4);var o=new pe(t,{width:i,height:r}),s=t instanceof Float32Array?S.FLOAT:S.UNSIGNED_BYTE;return new e(o,Object.assign(fe,n||{width:i,height:r,type:s}))},e.addToCache=function(t,e){e&&(-1===t.textureCacheIds.indexOf(e)&&t.textureCacheIds.push(e),nt[e]&&console.warn("BaseTexture added to the cache with an id ["+e+"] that already had an entry"),nt[e]=t)},e.removeFromCache=function(t){if("string"==typeof t){var e=nt[t];if(e){var i=e.textureCacheIds.indexOf(t);return i>-1&&e.textureCacheIds.splice(i,1),delete nt[t],e}}else if(t&&t.textureCacheIds){for(var r=0;r]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*(?:\s(width|height)=('|")(\d*(?:\.\d+)?)(?:px)?('|"))[^>]*>/i;var xe=function(t){function e(e,i){if(i=i||{},!(e instanceof HTMLVideoElement)){var r=document.createElement("video");r.setAttribute("preload","auto"),r.setAttribute("webkit-playsinline",""),r.setAttribute("playsinline",""),"string"==typeof e&&(e=[e]),t.crossOrigin(r,e[0].src||e[0],i.crossorigin);for(var n=0;n0&&!1===t.paused&&!1===t.ended&&t.readyState>2},e.prototype._isSourceReady=function(){return 3===this.source.readyState||4===this.source.readyState},e.prototype._onPlayStart=function(){this.valid||this._onCanPlay(),!this._isAutoUpdating&&this.autoUpdate&&(re.shared.add(this.update,this),this._isAutoUpdating=!0)},e.prototype._onPlayStop=function(){this._isAutoUpdating&&(re.shared.remove(this.update,this),this._isAutoUpdating=!1)},e.prototype._onCanPlay=function(){var t=this.source;t.removeEventListener("canplay",this._onCanPlay),t.removeEventListener("canplaythrough",this._onCanPlay);var e=this.valid;this.resize(t.videoWidth,t.videoHeight),!e&&this._resolve&&(this._resolve(this),this._resolve=null),this._isSourcePlaying()?this._onPlayStart():this.autoPlay&&t.play()},e.prototype.dispose=function(){this._isAutoUpdating&&re.shared.remove(this.update,this),this.source&&(this.source.removeEventListener("error",this._onError,!0),this.source.pause(),this.source.src="",this.source.load()),t.prototype.dispose.call(this)},i.autoUpdate.get=function(){return this._autoUpdate},i.autoUpdate.set=function(t){t!==this._autoUpdate&&(this._autoUpdate=t,!this._autoUpdate&&this._isAutoUpdating?(re.shared.remove(this.update,this),this._isAutoUpdating=!1):this._autoUpdate&&!this._isAutoUpdating&&(re.shared.add(this.update,this),this._isAutoUpdating=!0))},i.updateFPS.get=function(){return this._updateFPS},i.updateFPS.set=function(t){t!==this._updateFPS&&(this._updateFPS=t)},e.test=function(t,i){return t instanceof HTMLVideoElement||e.TYPES.indexOf(i)>-1},Object.defineProperties(e.prototype,i),e}(ue);xe.TYPES=["mp4","m4v","webm","ogg","ogv","h264","avi","mov"];var be=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.test=function(t){return!!window.createImageBitmap&&t instanceof ImageBitmap},e}(ue);ce.push(le,be,ye,xe,me,pe,_e,ve);var we={INSTALLED:ce,autoDetectResource:de,ArrayResource:ve,BufferResource:pe,CanvasResource:ye,CubeResource:_e,ImageResource:le,ImageBitmapResource:be,SVGResource:me,VideoResource:xe,Resource:ae,BaseImageResource:ue},Te=function(t){this.renderer=t};Te.prototype.destroy=function(){this.renderer=null};var Ee=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.upload=function(t,e,i){var r=t.gl;return r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,e.premultiplyAlpha),i.width===e.width&&i.height===e.height?r.texSubImage2D(e.target,0,0,0,e.width,e.height,e.format,e.type,this.data):(i.width=e.width,i.height=e.height,r.texImage2D(e.target,0,r.DEPTH_COMPONENT16,e.width,e.height,0,e.format,e.type,this.data)),!0},e}(pe),Se=function(t,e){this.width=Math.ceil(t||100),this.height=Math.ceil(e||100),this.stencil=!1,this.depth=!1,this.dirtyId=0,this.dirtyFormat=0,this.dirtySize=0,this.depthTexture=null,this.colorTextures=[],this.glFramebuffers={},this.disposeRunner=new Jt("disposeFramebuffer",2)},Pe={colorTexture:{configurable:!0}};Pe.colorTexture.get=function(){return this.colorTextures[0]},Se.prototype.addColorTexture=function(t,e){return void 0===t&&(t=0),this.colorTextures[t]=e||new ge(null,{scaleMode:0,resolution:1,mipmap:!1,width:this.width,height:this.height}),this.dirtyId++,this.dirtyFormat++,this},Se.prototype.addDepthTexture=function(t){return this.depthTexture=t||new ge(new Ee(null,{width:this.width,height:this.height}),{scaleMode:0,resolution:1,width:this.width,height:this.height,mipmap:!1,format:T.DEPTH_COMPONENT,type:S.UNSIGNED_SHORT}),this.dirtyId++,this.dirtyFormat++,this},Se.prototype.enableDepth=function(){return this.depth=!0,this.dirtyId++,this.dirtyFormat++,this},Se.prototype.enableStencil=function(){return this.stencil=!0,this.dirtyId++,this.dirtyFormat++,this},Se.prototype.resize=function(t,e){if(t=Math.ceil(t),e=Math.ceil(e),t!==this.width||e!==this.height){this.width=t,this.height=e,this.dirtyId++,this.dirtySize++;for(var i=0;i-1&&e.textureCacheIds.splice(i,1),delete rt[t],e}}else if(t&&t.textureCacheIds){for(var r=0;rthis.baseTexture.width,s=i+n>this.baseTexture.height;if(o||s){var a=o&&s?"and":"or",h="X: "+e+" + "+r+" = "+(e+r)+" > "+this.baseTexture.width,u="Y: "+i+" + "+n+" = "+(i+n)+" > "+this.baseTexture.height;throw new Error("Texture Error: frame does not fit inside the base Texture dimensions: "+h+" "+a+" "+u)}this.valid=r&&n&&this.baseTexture.valid,this.trim||this.rotate||(this.orig=t),this.valid&&this.updateUvs()},i.rotate.get=function(){return this._rotate},i.rotate.set=function(t){this._rotate=t,this.valid&&this.updateUvs()},i.width.get=function(){return this.orig.width},i.height.get=function(){return this.orig.height},Object.defineProperties(e.prototype,i),e}(f.a);function Me(t){t.destroy=function(){},t.on=function(){},t.once=function(){},t.emit=function(){}}Ie.EMPTY=new Ie(new ge),Me(Ie.EMPTY),Me(Ie.EMPTY.baseTexture),Ie.WHITE=function(){var t=document.createElement("canvas");t.width=16,t.height=16;var e=t.getContext("2d");return e.fillStyle="white",e.fillRect(0,0,16,16),new Ie(new ge(new ye(t)))}(),Me(Ie.WHITE),Me(Ie.WHITE.baseTexture);var De=function(t){function e(e,i){var r=null;if(!(e instanceof Ae)){var n=arguments[1],o=arguments[2],s=arguments[3],a=arguments[4];console.warn("Please use RenderTexture.create("+n+", "+o+") instead of the ctor directly."),r=arguments[0],i=null,e=new Ae({width:n,height:o,scaleMode:s,resolution:a})}t.call(this,e,i),this.legacyRenderer=r,this.valid=!0,this.filterFrame=null,this.filterPoolKey=null,this.updateUvs()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.resize=function(t,e,i){void 0===i&&(i=!0),t=Math.ceil(t),e=Math.ceil(e),this.valid=t>0&&e>0,this._frame.width=this.orig.width=t,this._frame.height=this.orig.height=e,i&&this.baseTexture.resize(t,e),this.updateUvs()},e.prototype.setResolution=function(t){var e=this.baseTexture;e.resolution!==t&&(e.setResolution(t),this.resize(e.width,e.height,!1))},e.create=function(t){return"number"==typeof t&&(t={width:t,height:arguments[1],scaleMode:arguments[2],resolution:arguments[3]}),new e(new Ae(t))},e}(Ie),Re=function(t){this.texturePool={},this.textureOptions=t||{},this.enableFullScreen=!1,this._pixelsWidth=0,this._pixelsHeight=0};Re.prototype.createTexture=function(t,e){var i=new Ae(Object.assign({width:t,height:e,resolution:1},this.textureOptions));return new De(i)},Re.prototype.getOptimalTexture=function(t,e,i){void 0===i&&(i=1);var r=Re.SCREEN_KEY;t*=i,e*=i,this.enableFullScreen&&t===this._pixelsWidth&&e===this._pixelsHeight||(r=(65535&(t=$(t)))<<16|65535&(e=$(e))),this.texturePool[r]||(this.texturePool[r]=[]);var n=this.texturePool[r].pop();return n||(n=this.createTexture(t,e)),n.filterPoolKey=r,n.setResolution(i),n},Re.prototype.getFilterTexture=function(t,e){var i=this.getOptimalTexture(t.width,t.height,e||t.resolution);return i.filterFrame=t.filterFrame,i},Re.prototype.returnTexture=function(t){var e=t.filterPoolKey;t.filterFrame=null,this.texturePool[e].push(t)},Re.prototype.returnFilterTexture=function(t){this.returnTexture(t)},Re.prototype.clear=function(t){if(t=!1!==t)for(var e in this.texturePool){var i=this.texturePool[e];if(i)for(var r=0;r0&&t.height>0,i)for(var r=0;r1){for(var u=0;u=m.WEBGL2&&(i=t.getContext("webgl2",e)),i)this.webGLVersion=2;else if(this.webGLVersion=1,!(i=t.getContext("webgl",e)||t.getContext("experimental-webgl",e)))throw new Error("This browser does not support WebGL. Try using the canvas renderer");return this.gl=i,this.getExtensions(),i},e.prototype.getExtensions=function(){var t=this.gl;1===this.webGLVersion?Object.assign(this.extensions,{drawBuffers:t.getExtension("WEBGL_draw_buffers"),depthTexture:t.getExtension("WEBKIT_WEBGL_depth_texture"),loseContext:t.getExtension("WEBGL_lose_context"),vertexArrayObject:t.getExtension("OES_vertex_array_object")||t.getExtension("MOZ_OES_vertex_array_object")||t.getExtension("WEBKIT_OES_vertex_array_object"),anisotropicFiltering:t.getExtension("EXT_texture_filter_anisotropic"),uint32ElementIndex:t.getExtension("OES_element_index_uint"),floatTexture:t.getExtension("OES_texture_float"),floatTextureLinear:t.getExtension("OES_texture_float_linear"),textureHalfFloat:t.getExtension("OES_texture_half_float"),textureHalfFloatLinear:t.getExtension("OES_texture_half_float_linear")}):2===this.webGLVersion&&Object.assign(this.extensions,{anisotropicFiltering:t.getExtension("EXT_texture_filter_anisotropic"),colorBufferFloat:t.getExtension("EXT_color_buffer_float"),floatTextureLinear:t.getExtension("OES_texture_float_linear")})},e.prototype.handleContextLost=function(t){t.preventDefault()},e.prototype.handleContextRestored=function(){this.renderer.runners.contextChange.run(this.gl)},e.prototype.destroy=function(){var t=this.renderer.view;t.removeEventListener("webglcontextlost",this.handleContextLost),t.removeEventListener("webglcontextrestored",this.handleContextRestored),this.gl.useProgram(null),this.extensions.loseContext&&this.extensions.loseContext.loseContext()},e.prototype.postrender=function(){this.gl.flush()},e.prototype.validateContext=function(t){t.getContextAttributes().stencil||console.warn("Provided WebGL context does not have a stencil buffer, masks may not render correctly")},Object.defineProperties(e.prototype,i),e}(Te),$e=function(t){function e(e){t.call(this,e),this.managedFramebuffers=[],this.unknownFramebuffer=new Se(10,10)}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={size:{configurable:!0}};return e.prototype.contextChange=function(){var t=this.gl=this.renderer.gl;if(this.CONTEXT_UID=this.renderer.CONTEXT_UID,this.current=this.unknownFramebuffer,this.viewport=new Lt,this.hasMRT=!0,this.writeDepthTexture=!0,this.disposeAll(!0),1===this.renderer.context.webGLVersion){var e=this.renderer.context.extensions.drawBuffers,i=this.renderer.context.extensions.depthTexture;d.PREFER_ENV===m.WEBGL_LEGACY&&(e=null,i=null),e?t.drawBuffers=function(t){return e.drawBuffersWEBGL(t)}:(this.hasMRT=!1,t.drawBuffers=function(){}),i||(this.writeDepthTexture=!1)}},e.prototype.bind=function(t,e){var i=this.gl;if(t){var r=t.glFramebuffers[this.CONTEXT_UID]||this.initFramebuffer(t);this.current!==t&&(this.current=t,i.bindFramebuffer(i.FRAMEBUFFER,r.framebuffer)),r.dirtyId!==t.dirtyId&&(r.dirtyId=t.dirtyId,r.dirtyFormat!==t.dirtyFormat?(r.dirtyFormat=t.dirtyFormat,this.updateFramebuffer(t)):r.dirtySize!==t.dirtySize&&(r.dirtySize=t.dirtySize,this.resizeFramebuffer(t)));for(var n=0;n1&&e.drawBuffers(n),t.depthTexture)&&this.writeDepthTexture){var a=t.depthTexture;this.renderer.texture.bind(a,0),e.framebufferTexture2D(e.FRAMEBUFFER,e.DEPTH_ATTACHMENT,e.TEXTURE_2D,a._glTextures[this.CONTEXT_UID].texture,0)}i.stencil||!t.stencil&&!t.depth||(i.stencil=e.createRenderbuffer(),e.bindRenderbuffer(e.RENDERBUFFER,i.stencil),t.depthTexture||e.framebufferRenderbuffer(e.FRAMEBUFFER,e.DEPTH_STENCIL_ATTACHMENT,e.RENDERBUFFER,i.stencil),e.renderbufferStorage(e.RENDERBUFFER,e.DEPTH_STENCIL,t.width,t.height))},e.prototype.disposeFramebuffer=function(t,e){var i=t.glFramebuffers[this.CONTEXT_UID],r=this.gl;if(i){delete t.glFramebuffers[this.CONTEXT_UID];var n=this.managedFramebuffers.indexOf(t);n>=0&&this.managedFramebuffers.splice(n,1),t.disposeRunner.remove(this),e||(r.deleteFramebuffer(i.framebuffer),i.stencil&&r.deleteRenderbuffer(i.stencil))}},e.prototype.disposeAll=function(t){var e=this.managedFramebuffers;this.managedFramebuffers=[];for(var i=0;i=r.data.byteLength)e.bufferSubData(o,0,r.data);else{var s=r.static?e.STATIC_DRAW:e.DYNAMIC_DRAW;n.byteLength=r.data.byteLength,e.bufferData(o,r.data,s)}}}},e.prototype.checkCompatibility=function(t,e){var i=t.attributes,r=e.attributeData;for(var n in r)if(!i[n])throw new Error('shader and geometry incompatible, geometry missing the "'+n+'" attribute')},e.prototype.getSignature=function(t,e){var i=t.attributes,r=e.attributeData,n=["g",t.id];for(var o in i)r[o]&&n.push(o);return n.join("-")},e.prototype.initGeometryVao=function(t,e){this.checkCompatibility(t,e);var i=this.gl,r=this.CONTEXT_UID,n=this.getSignature(t,e),o=t.glVertexArrayObjects[this.CONTEXT_UID],s=o[n];if(s)return o[e.id]=s,s;var a=t.buffers,h=t.attributes,u={},l={};for(var c in a)u[c]=0,l[c]=0;for(var d in h)!h[d].size&&e.attributeData[d]?h[d].size=e.attributeData[d].size:h[d].size||console.warn("PIXI Geometry attribute '"+d+"' size cannot be determined (likely the bound shader does not have the attribute)"),u[h[d].buffer]+=h[d].size*ei[h[d].type];for(var p in h){var f=h[p],g=f.size;void 0===f.stride&&(u[f.buffer]===g*ei[f.type]?f.stride=0:f.stride=u[f.buffer]),void 0===f.start&&(f.start=l[f.buffer],l[f.buffer]+=g*ei[f.type])}s=i.createVertexArray(),i.bindVertexArray(s);for(var v=0;v=m.WEBGL2&&(t=e.getContext("webgl2",{})),t||((t=e.getContext("webgl",{})||e.getContext("experimental-webgl",{}))?t.getExtension("WEBGL_draw_buffers"):t=null),ui=t}return ui}function ci(t,e,i){if("precision"!==t.substring(0,9)){var r=e;return e===D.HIGH&&i!==D.HIGH&&(r=D.MEDIUM),"precision "+r+" float;\n"+t}return i!==D.HIGH&&"precision highp"===t.substring(0,15)?t.replace("precision highp","precision mediump"):t}var di={float:1,vec2:2,vec3:3,vec4:4,int:1,ivec2:2,ivec3:3,ivec4:4,bool:1,bvec2:2,bvec3:3,bvec4:4,mat2:4,mat3:9,mat4:16,sampler2D:1};function pi(t){return di[t]}var fi=null,gi={FLOAT:"float",FLOAT_VEC2:"vec2",FLOAT_VEC3:"vec3",FLOAT_VEC4:"vec4",INT:"int",INT_VEC2:"ivec2",INT_VEC3:"ivec3",INT_VEC4:"ivec4",BOOL:"bool",BOOL_VEC2:"bvec2",BOOL_VEC3:"bvec3",BOOL_VEC4:"bvec4",FLOAT_MAT2:"mat2",FLOAT_MAT3:"mat3",FLOAT_MAT4:"mat4",SAMPLER_2D:"sampler2D",SAMPLER_CUBE:"samplerCube",SAMPLER_2D_ARRAY:"sampler2DArray"};function vi(t,e){if(!fi){var i=Object.keys(gi);fi={};for(var r=0;r0&&(e+="\nelse "),ie.name?1:-1});for(var u=0;u>=1,i++;this.stateId=t.data}for(var r=0;rthis.checkCountMax&&(this.checkCount=0,this.run()))},e.prototype.run=function(){for(var t=this.renderer.texture,e=t.managedTextures,i=!1,r=0;rthis.maxIdle&&(t.destroyTexture(n,!0),e[r]=null,i=!0)}if(i){for(var o=0,s=0;s=0;i--)this.unload(t.children[i])},e}(Te),Qi=function(t){this.texture=t,this.width=-1,this.height=-1,this.dirtyId=-1,this.dirtyStyleId=-1,this.mipmap=!1,this.wrapMode=33071,this.type=6408,this.internalFormat=5121},$i=function(t){function e(e){t.call(this,e),this.boundTextures=[],this.currentLocation=-1,this.managedTextures=[],this._unknownBoundTextures=!1,this.unknownTexture=new ge}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.contextChange=function(){var t=this.gl=this.renderer.gl;this.CONTEXT_UID=this.renderer.CONTEXT_UID,this.webGLVersion=this.renderer.context.webGLVersion;var e=t.getParameter(t.MAX_TEXTURE_IMAGE_UNITS);this.boundTextures.length=e;for(var i=0;i=1,e.wrapMode=t.wrapMode):(e.mipmap=0,e.wrapMode=C),t.resource&&t.resource.style(this.renderer,t,e)||this.setStyle(t,e),e.dirtyStyleId=t.dirtyStyleId)},e.prototype.setStyle=function(t,e){var i=this.gl;if(e.mipmap&&i.generateMipmap(t.target),i.texParameteri(t.target,i.TEXTURE_WRAP_S,e.wrapMode),i.texParameteri(t.target,i.TEXTURE_WRAP_T,e.wrapMode),e.mipmap){i.texParameteri(t.target,i.TEXTURE_MIN_FILTER,t.scaleMode?i.LINEAR_MIPMAP_LINEAR:i.NEAREST_MIPMAP_NEAREST);var r=this.renderer.context.extensions.anisotropicFiltering;if(r&&t.anisotropicLevel>0&&t.scaleMode===P){var n=Math.min(t.anisotropicLevel,i.getParameter(r.MAX_TEXTURE_MAX_ANISOTROPY_EXT));i.texParameterf(t.target,r.TEXTURE_MAX_ANISOTROPY_EXT,n)}}else i.texParameteri(t.target,i.TEXTURE_MIN_FILTER,t.scaleMode?i.LINEAR:i.NEAREST);i.texParameteri(t.target,i.TEXTURE_MAG_FILTER,t.scaleMode?i.LINEAR:i.NEAREST)},e}(Te),tr=new Et,er=function(t){function e(e,i){t.call(this),(i=Object.assign({},d.RENDER_OPTIONS,i)).roundPixels&&(d.ROUND_PIXELS=i.roundPixels,vt("5.0.0","Renderer roundPixels option is deprecated, please use PIXI.settings.ROUND_PIXELS",2)),this.options=i,this.type=x.UNKNOWN,this.screen=new Lt(0,0,i.width,i.height),this.view=i.view||document.createElement("canvas"),this.resolution=i.resolution||d.RESOLUTION,this.transparent=i.transparent,this.autoDensity=i.autoDensity||i.autoResize||!1,this.preserveDrawingBuffer=i.preserveDrawingBuffer,this.clearBeforeRender=i.clearBeforeRender,this._backgroundColor=0,this._backgroundColorRgba=[0,0,0,0],this._backgroundColorString="#000000",this.backgroundColor=i.backgroundColor||this._backgroundColor,this._tempDisplayObjectParent=new Vt,this._lastObjectRendered=this._tempDisplayObjectParent,this.plugins={}}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={width:{configurable:!0},height:{configurable:!0},backgroundColor:{configurable:!0}};return e.prototype.initPlugins=function(t){for(var e in t)this.plugins[e]=new t[e](this)},i.width.get=function(){return this.view.width},i.height.get=function(){return this.view.height},e.prototype.resize=function(t,e){this.screen.width=t,this.screen.height=e,this.view.width=t*this.resolution,this.view.height=e*this.resolution,this.autoDensity&&(this.view.style.width=t+"px",this.view.style.height=e+"px")},e.prototype.generateTexture=function(t,e,i,r){0===(r=r||t.getLocalBounds()).width&&(r.width=1),0===r.height&&(r.height=1);var n=De.create(0|r.width,0|r.height,e,i);return tr.tx=-r.x,tr.ty=-r.y,this.render(t,n,!1,tr,!!t.parent),n},e.prototype.destroy=function(t){for(var e in this.plugins)this.plugins[e].destroy(),this.plugins[e]=null;t&&this.view.parentNode&&this.view.parentNode.removeChild(this.view),this.plugins=null,this.type=x.UNKNOWN,this.view=null,this.screen=null,this.resolution=0,this.transparent=!1,this.autoDensity=!1,this.blendModes=null,this.options=null,this.preserveDrawingBuffer=!1,this.clearBeforeRender=!1,this._backgroundColor=0,this._backgroundColorRgba=null,this._backgroundColorString=null,this._tempDisplayObjectParent=null,this._lastObjectRendered=null},i.backgroundColor.get=function(){return this._backgroundColor},i.backgroundColor.set=function(t){this._backgroundColor=t,this._backgroundColorString=j(t),U(t,this._backgroundColorRgba)},Object.defineProperties(e.prototype,i),e}(f.a),ir=function(t){function e(i){void 0===i&&(i={}),t.call(this,"WebGL",i),i=this.options,this.type=x.WEBGL,this.gl=null,this.CONTEXT_UID=0,this.runners={destroy:new Jt("destroy"),contextChange:new Jt("contextChange",1),reset:new Jt("reset"),update:new Jt("update"),postrender:new Jt("postrender"),prerender:new Jt("prerender"),resize:new Jt("resize",2)},this.globalUniforms=new Ve({projectionMatrix:new Et},!0),this.addSystem(Ni,"mask").addSystem(Qe,"context").addSystem(Zi,"state").addSystem(Gi,"shader").addSystem($i,"texture").addSystem(ii,"geometry").addSystem($e,"framebuffer").addSystem(Bi,"stencil").addSystem(Ui,"projection").addSystem(Ki,"textureGC").addSystem(qe,"filter").addSystem(Hi,"renderTexture").addSystem(Ze,"batch"),this.initPlugins(e.__plugins),i.context?this.context.initFromContext(i.context):this.context.initFromOptions({alpha:this.transparent,antialias:i.antialias,premultipliedAlpha:this.transparent&&"notMultiplied"!==this.transparent,stencil:!0,preserveDrawingBuffer:i.preserveDrawingBuffer,powerPreference:this.options.powerPreference}),this.renderingToScreen=!0,N(2===this.context.webGLVersion?"WebGL 2":"WebGL 1"),this.resize(this.options.width,this.options.height)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.create=function(t){if(B())return new e(t);throw new Error('WebGL unsupported in this browser, use "pixi.js-legacy" for fallback canvas2d support.')},e.prototype.addSystem=function(t,e){e||(e=t.name);var i=new t(this);if(this[e])throw new Error('Whoops! The name "'+e+'" is already in use');for(var r in this[e]=i,this.runners)this.runners[r].add(i);return this},e.prototype.render=function(t,e,i,r,n){if(this.renderingToScreen=!e,this.runners.prerender.run(),this.emit("prerender"),this.projection.transform=r,!this.context.isLost){if(e||(this._lastObjectRendered=t),!n){var o=t.parent;t.parent=this._tempDisplayObjectParent,t.updateTransform(),t.parent=o}this.renderTexture.bind(e),this.batch.currentRenderer.start(),(void 0!==i?i:this.clearBeforeRender)&&this.renderTexture.clear(),t.render(this),this.batch.currentRenderer.flush(),e&&e.baseTexture.update(),this.runners.postrender.run(),this.projection.transform=null,this.emit("postrender")}},e.prototype.resize=function(e,i){t.prototype.resize.call(this,e,i),this.runners.resize.run(e,i)},e.prototype.reset=function(){return this.runners.reset.run(),this},e.prototype.clear=function(){this.framebuffer.bind(),this.framebuffer.clear()},e.prototype.destroy=function(e){for(var i in this.runners.destroy.run(),this.runners)this.runners[i].destroy();t.prototype.destroy.call(this,e),this.gl=null},e.registerPlugin=function(t,i){e.__plugins=e.__plugins||{},e.__plugins[t]=i},e}(er);var rr="attribute vec2 aVertexPosition;\n\nuniform mat3 projectionMatrix;\n\nvarying vec2 vTextureCoord;\n\nuniform vec4 inputSize;\nuniform vec4 outputFrame;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aVertexPosition * max(outputFrame.zw, vec2(0.)) + outputFrame.xy;\n\n return vec4((projectionMatrix * vec3(position, 1.0)).xy, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aVertexPosition * (outputFrame.zw * inputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n",nr=function(){this.textures=[],this.ids=[],this.blend=0,this.textureCount=0,this.start=0,this.size=0,this.type=4},or=function(t){this.rawBinaryData=new ArrayBuffer(t),this.uint32View=new Uint32Array(this.rawBinaryData),this.float32View=new Float32Array(this.rawBinaryData)},sr={int8View:{configurable:!0},uint8View:{configurable:!0},int16View:{configurable:!0},uint16View:{configurable:!0},int32View:{configurable:!0}};sr.int8View.get=function(){return this._int8View||(this._int8View=new Int8Array(this.rawBinaryData)),this._int8View},sr.uint8View.get=function(){return this._uint8View||(this._uint8View=new Uint8Array(this.rawBinaryData)),this._uint8View},sr.int16View.get=function(){return this._int16View||(this._int16View=new Int16Array(this.rawBinaryData)),this._int16View},sr.uint16View.get=function(){return this._uint16View||(this._uint16View=new Uint16Array(this.rawBinaryData)),this._uint16View},sr.int32View.get=function(){return this._int32View||(this._int32View=new Int32Array(this.rawBinaryData)),this._int32View},or.prototype.view=function(t){return this[t+"View"]},or.prototype.destroy=function(){this.rawBinaryData=null,this._int8View=null,this._uint8View=null,this._int16View=null,this._uint16View=null,this._int32View=null,this.uint32View=null,this.float32View=null},or.sizeOf=function(t){switch(t){case"int8":case"uint8":return 1;case"int16":case"uint16":return 2;case"int32":case"uint32":case"float32":return 4;default:throw new Error(t+" isn't a valid view type")}},Object.defineProperties(or.prototype,sr);var ar=function(t){function e(e){t.call(this,e),this.shaderGenerator=null,this.geometryClass=null,this.vertexSize=null,this.state=Ci.for2d(),this.size=8e3,this._vertexCount=0,this._indexCount=0,this._bufferedElements=[],this._bufferSize=0,this._shader=null,this._packedGeometries=[],this._packedGeometryPoolSize=2,this._flushId=0,this._drawCalls=[];for(var i=0;ithis.size&&this.flush(),this._vertexCount+=t.vertexData.length/2,this._indexCount+=t.indices.length,this._bufferedElements[this._bufferSize++]=t)},e.prototype.flush=function(){if(0!==this._vertexCount){var t,e,i=this.getAttributeBuffer(this._vertexCount),r=this.getIndexBuffer(this._indexCount),n=this.renderer.gl,o=this._bufferedElements,s=this._drawCalls,a=this.MAX_TEXTURES,h=this._packedGeometries,u=this.vertexSize,l=this.renderer.textureGC.count,c=0,p=0,f=0,g=s[0],v=0,y=-1;g.textureCount=0,g.start=0,g.blend=y;var _,m=++ge._globalBatch;for(_=0;_0&&(e+="\nelse "),i=0;c--){var d=l[c],p=this.processInteractive(t,d,i,r,h,!0);if(p){if(!d.parent)continue;h=!1,p&&(t.target&&(r=!1),a=!0)}}n&&(r&&!t.target&&!e.hitArea&&e.containsPoint&&e.containsPoint(s)&&(a=!0),e.interactive&&(a&&!t.target&&(t.target=e),i&&i(t,e,!!a)));var f=this.delayedEvents;if(f.length&&!o){var g=f.length;this.delayedEvents=[];for(var v=0;vthis.maxSegments&&(i=this.maxSegments),i}},Ar=function(){this.reset()};Ar.prototype.clone=function(){var t=new Ar;return t.color=this.color,t.alpha=this.alpha,t.texture=this.texture,t.matrix=this.matrix,t.visible=this.visible,t},Ar.prototype.reset=function(){this.color=16777215,this.alpha=1,this.texture=Ie.WHITE,this.matrix=null,this.visible=!1},Ar.prototype.destroy=function(){this.texture=null,this.matrix=null};var Cr=function(t,e,i,r){void 0===e&&(e=null),void 0===i&&(i=null),void 0===r&&(r=null),this.shape=t,this.lineStyle=i,this.fillStyle=e,this.matrix=r,this.type=t.type,this.points=[],this.holes=[]};Cr.prototype.clone=function(){return new Cr(this.shape,this.fillStyle,this.lineStyle,this.matrix)},Cr.prototype.destroy=function(){this.shape=null,this.holes.length=0,this.holes=null,this.points.length=0,this.points=null,this.lineStyle=null,this.fillStyle=null};var Or={build:function(t){var e,i,r=t.shape,n=t.points,o=r.x,s=r.y;if(n.length=0,t.type===Tt.CIRC?(e=r.radius,i=r.radius):(e=r.width,i=r.height),0!==e&&0!==i){var a=Math.floor(30*Math.sqrt(r.radius))||Math.floor(15*Math.sqrt(r.width+r.height));a/=2.3;for(var h=2*Math.PI/a,u=0;u196*v*v?(A=T-S,C=E-P,O=Math.sqrt(A*A+C*C),A/=O,C/=O,A*=v,C*=v,d.push(m-A*M,x-C*M),d.push(m+A*D,x+C*D),d.push(m-A*D*M,x-C*M),f++):(d.push(m+(H-m)*M,x+(z-x)*M),d.push(m-(H-m)*D,x-(z-x)*D))}}y=r[2*(p-2)],_=r[2*(p-2)+1],m=r[2*(p-1)],x=r[2*(p-1)+1],T=-(_-x),E=y-m,O=Math.sqrt(T*T+E*E),T/=O,E/=O,T*=v,E*=v,d.push(m-T*M,x-E*M),d.push(m+T*D,x+E*D);for(var G=e.indices,Y=0;Y=6){for(var s=[],a=0;a0&&(this.invalidate(),this.clearDirty++,this.graphicsData.length=0),this},e.prototype.drawShape=function(t,e,i,r){var n=new Cr(t,e,i,r);return this.graphicsData.push(n),this.dirty++,this},e.prototype.drawHole=function(t,e){if(!this.graphicsData.length)return null;var i=new Cr(t,null,null,e),r=this.graphicsData[this.graphicsData.length-1];return i.lineStyle=r.lineStyle,r.holes.push(i),this.dirty++,this},e.prototype.destroy=function(e){t.prototype.destroy.call(this,e);for(var i=0;i0){var a=(r=this.batches[this.batches.length-1]).style;n=a.texture.baseTexture,o=a.color+a.alpha,s=!!a.native}for(var h=this.shapeIndex;h0&&(r=null)),r||(r=Fr.pop()||new Ur,this.batches.push(r),g.wrapMode=O,n=g,o=f.color+f.alpha,s=f.native,r.style=f,r.start=v,r.attribStart=y);var _=this.points.length/2;if(0===p)u.holes.length?(this.processHoles(u.holes),Mr.triangulate(u,this)):l.triangulate(u,this);else{Ir(u,this);for(var m=0;m0&&(o=Lr.pop()||new nr,this.drawCalls.push(o)),o.start=c,o.size=0,o.textureCount=0,o.type=l),g.touched=1,g._batchEnabled=t,g._id=s,g.wrapMode=10497,o.textures[o.textureCount++]=g,s++)),o.size+=p.size,c+=p.size,h=g._id,this.addColors(r,f.color,f.alpha,p.attribSize),this.addTextureIds(n,h,p.attribSize)}ge._globalBatch=t;for(var v=this.points,y=new ArrayBuffer(3*v.length*4),_=new Float32Array(y),m=new Uint32Array(y),x=0,b=0;be?o+a:e,i=sr?s+h:r;else if(c===Tt.CIRC)o=n.x,s=n.y,t=o-(a=n.radius+d/2)e?o+a:e,i=s-(h=n.radius+d/2)r?s+h:r;else if(c===Tt.ELIP)o=n.x,s=n.y,t=o-(a=n.width+d/2)e?o+a:e,i=s-(h=n.height+d/2)r?s+h:r;else for(var p=n.points,f=0,g=0,v=0,y=0,_=0,m=0,x=0,b=0,w=0;w+2e?x+_:e,i=(b=(g+s)/2)-(m=(h/a*v+y)/2)r?b+m:r)}else t=0,e=0,i=0,r=0;var T=this.boundsPadding;this._bounds.minX=t-T,this._bounds.maxX=e+T,this._bounds.minY=i-T,this._bounds.maxY=r+T},e.prototype.transformPoints=function(t,e){for(var i=0;i>16)+(65280&e)+((255&e)<<16),i);r-- >0;)t.push(n)},e.prototype.addTextureIds=function(t,e,i){for(;i-- >0;)t.push(e)},e.prototype.addUvs=function(t,e,i,r,n,o){for(var s=0,a=e.length,h=i.frame;sl*a}},Gr.arc=function(t,e,i,r,n,o,s,a,h){for(var u=s-o,l=Pr._segmentsCount(Math.abs(u)*n,40*Math.ceil(Math.abs(u)/xt)),c=u/(2*l),d=2*c,p=Math.cos(c),f=Math.sin(c),g=l-1,v=g%1/g,y=0;y<=g;++y){var _=c+o+d*(y+v*y),m=Math.cos(_),x=-Math.sin(_);h.push((p*m+f*x)*n+i,(p*-x+f*m)*n+r)}};var Yr=function(t){function e(e,i,r,n,o,s){o=o||n/2;for(var a=-1*Math.PI/2+s,h=2*r,u=xt/h,l=[],c=0;c0&&r>0;return a?(n&&(n=n.clone()).invert(),Object.assign(this._lineStyle,{color:i,width:t,alpha:r,matrix:n,texture:e,alignment:o,native:s,visible:a})):this._lineStyle.reset(),this},e.prototype.startPoly=function(){if(this.currentPath){var t=this.currentPath.points,e=this.currentPath.points.length;e>2&&(this.drawShape(this.currentPath),this.currentPath=new Ht,this.currentPath.closeStroke=!1,this.currentPath.points.push(t[e-2],t[e-1]))}else this.currentPath=new Ht,this.currentPath.closeStroke=!1},e.prototype.finishPoly=function(){this.currentPath&&(this.currentPath.points.length>2?(this.drawShape(this.currentPath),this.currentPath=null):this.currentPath.points.length=0)},e.prototype.moveTo=function(t,e){return this.startPoly(),this.currentPath.points[0]=t,this.currentPath.points[1]=e,this},e.prototype.lineTo=function(t,e){this.currentPath||this.moveTo(0,0);var i=this.currentPath.points,r=i[i.length-2],n=i[i.length-1];return r===t&&n===e||i.push(t,e),this},e.prototype._initCurve=function(t,e){void 0===t&&(t=0),void 0===e&&(e=0),this.currentPath?0===this.currentPath.points.length&&(this.currentPath.points=[t,e]):this.moveTo(t,e)},e.prototype.quadraticCurveTo=function(t,e,i,r){this._initCurve();var n=this.currentPath.points;return 0===n.length&&this.moveTo(0,0),Xr.curveTo(t,e,i,r,n),this},e.prototype.bezierCurveTo=function(t,e,i,r,n,o){return this._initCurve(),zr.curveTo(t,e,i,r,n,o,this.currentPath.points),this},e.prototype.arcTo=function(t,e,i,r,n){this._initCurve(t,e);var o=this.currentPath.points,s=Gr.curveTo(t,e,i,r,n,o);if(s){var a=s.cx,h=s.cy,u=s.radius,l=s.startAngle,c=s.endAngle,d=s.anticlockwise;this.arc(a,h,u,l,c,d)}return this},e.prototype.arc=function(t,e,i,r,n,o){if(void 0===o&&(o=!1),r===n)return this;if(!o&&n<=r?n+=xt:o&&r<=n&&(r+=xt),0===n-r)return this;var s=t+Math.cos(r)*i,a=e+Math.sin(r)*i,h=this.geometry.closePointEps,u=this.currentPath?this.currentPath.points:null;if(u){var l=Math.abs(u[u.length-2]-s),c=Math.abs(u[u.length-1]-a);l0;return n?(r&&(r=r.clone()).invert(),Object.assign(this._fillStyle,{color:e,alpha:i,texture:t,matrix:r,visible:n})):this._fillStyle.reset(),this},e.prototype.endFill=function(){return this.finishPoly(),this._fillStyle.reset(),this},e.prototype.drawRect=function(t,e,i,r){return this.drawShape(new Lt(t,e,i,r))},e.prototype.drawRoundedRect=function(t,e,i,r,n){return this.drawShape(new zt(t,e,i,r,n))},e.prototype.drawCircle=function(t,e,i){return this.drawShape(new Ut(t,e,i))},e.prototype.drawEllipse=function(t,e,i,r){return this.drawShape(new jt(t,e,i,r))},e.prototype.drawPolygon=function(t){var e=arguments,i=t,r=!0;if(i.points&&(r=i.closeStroke,i=i.points),!Array.isArray(i)){i=new Array(arguments.length);for(var n=0;n>16&255)/255*y,g.tint[1]=(v>>8&255)/255*y,g.tint[2]=(255&v)/255*y,g.tint[3]=y,t.shader.bind(this.shader),t.geometry.bind(e,this.shader),t.state.set(this.state);for(var _=0;_>16)+(65280&n)+((255&n)<<16)}}},e.prototype.calculateVertices=function(){if(this._transformID!==this.transform._worldID){this._transformID=this.transform._worldID;for(var t=this.transform.worldTransform,e=t.a,i=t.b,r=t.c,n=t.d,o=t.tx,s=t.ty,a=this.geometry.points,h=this.vertexData,u=0,l=0;l=r&&Jr.x=n&&Jr.y>16)+(65280&t)+((255&t)<<16)},i.texture.get=function(){return this._texture},i.texture.set=function(t){this._texture!==t&&(this._texture=t||Ie.EMPTY,this._cachedTint=16777215,this._textureID=-1,this._textureTrimmedID=-1,t&&(t.baseTexture.valid?this._onTextureUpdate():t.once("update",this._onTextureUpdate,this)))},Object.defineProperties(e.prototype,i),e}(Vt),Qr=0,$r={align:"left",breakWords:!1,dropShadow:!1,dropShadowAlpha:1,dropShadowAngle:Math.PI/6,dropShadowBlur:0,dropShadowColor:"black",dropShadowDistance:5,fill:"black",fillGradientType:Qr,fillGradientStops:[],fontFamily:"Arial",fontSize:26,fontStyle:"normal",fontVariant:"normal",fontWeight:"normal",letterSpacing:0,lineHeight:0,lineJoin:"miter",miterLimit:10,padding:0,stroke:"black",strokeThickness:0,textBaseline:"alphabetic",trim:!1,whiteSpace:"pre",wordWrap:!1,wordWrapWidth:100,leading:0},tn=["serif","sans-serif","monospace","cursive","fantasy","system-ui"],en=function(t){this.styleID=0,this.reset(),sn(this,t,t)},rn={align:{configurable:!0},breakWords:{configurable:!0},dropShadow:{configurable:!0},dropShadowAlpha:{configurable:!0},dropShadowAngle:{configurable:!0},dropShadowBlur:{configurable:!0},dropShadowColor:{configurable:!0},dropShadowDistance:{configurable:!0},fill:{configurable:!0},fillGradientType:{configurable:!0},fillGradientStops:{configurable:!0},fontFamily:{configurable:!0},fontSize:{configurable:!0},fontStyle:{configurable:!0},fontVariant:{configurable:!0},fontWeight:{configurable:!0},letterSpacing:{configurable:!0},lineHeight:{configurable:!0},leading:{configurable:!0},lineJoin:{configurable:!0},miterLimit:{configurable:!0},padding:{configurable:!0},stroke:{configurable:!0},strokeThickness:{configurable:!0},textBaseline:{configurable:!0},trim:{configurable:!0},whiteSpace:{configurable:!0},wordWrap:{configurable:!0},wordWrapWidth:{configurable:!0}};function nn(t){return"number"==typeof t?j(t):("string"==typeof t&&0===t.indexOf("0x")&&(t=t.replace("0x","#")),t)}function on(t){if(Array.isArray(t)){for(var e=0;e=0;i--){var r=e[i].trim();!/([\"\'])[^\'\"]+\1/.test(r)&&tn.indexOf(r)<0&&(r='"'+r+'"'),e[i]=r}return this.fontStyle+" "+this.fontVariant+" "+this.fontWeight+" "+t+" "+e.join(",")},Object.defineProperties(en.prototype,rn);var an=function(t,e,i,r,n,o,s,a,h){this.text=t,this.style=e,this.width=i,this.height=r,this.lines=n,this.lineWidths=o,this.lineHeight=s,this.maxLineWidth=a,this.fontProperties=h};an.measureText=function(t,e,i,r){void 0===r&&(r=an._canvas),i=null==i?e.wordWrap:i;var n=e.toFontString(),o=an.measureFont(n);0===o.fontSize&&(o.fontSize=e.fontSize,o.ascent=e.fontSize);var s=r.getContext("2d");s.font=n;for(var a=(i?an.wordWrap(t,e,r):t).split(/(?:\r\n|\r|\n)/),h=new Array(a.length),u=0,l=0;lp)if(""!==o&&(s+=an.addLine(o),o="",n=0),an.canBreakWords(v,e.breakWords))for(var x=v.split(""),b=0;bp&&(s+=an.addLine(o),d=!1,o="",n=0),o+=w,n+=P}else{o.length>0&&(s+=an.addLine(o),o="",n=0);var A=g===f.length-1;s+=an.addLine(v,!A),d=!1,o="",n=0}else m+n>p&&(d=!1,s+=an.addLine(o),o="",n=0),(o.length>0||!an.isBreakingSpace(v)||d)&&(o+=v,n+=m)}return s+=an.addLine(o,!1)},an.addLine=function(t,e){return void 0===e&&(e=!0),t=an.trimRight(t),t=e?t+"\n":t},an.getFromCache=function(t,e,i,r){var n=i[t];if(void 0===n){var o=t.length*e;n=r.measureText(t).width+o,i[t]=n}return n},an.collapseSpaces=function(t){return"normal"===t||"pre-line"===t},an.collapseNewlines=function(t){return"normal"===t},an.trimRight=function(t){if("string"!=typeof t)return"";for(var e=t.length-1;e>=0;e--){var i=t[e];if(!an.isBreakingSpace(i))break;t=t.slice(0,-1)}return t},an.isNewline=function(t){return"string"==typeof t&&an._newlines.indexOf(t.charCodeAt(0))>=0},an.isBreakingSpace=function(t){return"string"==typeof t&&an._breakingSpaces.indexOf(t.charCodeAt(0))>=0},an.tokenize=function(t){var e=[],i="";if("string"!=typeof t)return e;for(var r=0;rs;--c){for(var g=0;g0};var dn=function(t){var e=this;this.limiter=new cn(d.UPLOADS_PER_FRAME),this.renderer=t,this.uploadHookHelper=null,this.queue=[],this.addHooks=[],this.uploadHooks=[],this.completes=[],this.ticking=!1,this.delayedTick=function(){e.queue&&e.prepareItems()},this.registerFindHook(_n),this.registerFindHook(mn),this.registerFindHook(pn),this.registerFindHook(fn),this.registerFindHook(gn),this.registerUploadHook(vn),this.registerUploadHook(yn)};function pn(t,e){var i=!1;if(t&&t._textures&&t._textures.length)for(var r=0;r=0;r--)this.add(t.children[r]);return this},dn.prototype.destroy=function(){this.ticking&&re.system.remove(this.tick,this),this.ticking=!1,this.addHooks=null,this.uploadHooks=null,this.renderer=null,this.completes=null,this.queue=null,this.limiter=null,this.uploadHookHelper=null};var xn=function(t){function e(e){t.call(this,e),this.uploadHookHelper=this.renderer,this.registerFindHook(Tn),this.registerUploadHook(bn),this.registerUploadHook(wn)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(dn);function bn(t,e){return e instanceof ge&&(e._glTextures[t.CONTEXT_UID]||t.texture.bind(e),!0)}function wn(t,e){return e instanceof qr&&((e.dirty||e.clearDirty||!e._webGL[t.plugins.graphics.CONTEXT_UID])&&t.plugins.graphics.updateGraphics(e),!0)}function Tn(t,e){return t instanceof qr&&(e.push(t),!0)}var En=function(t){this.maxMilliseconds=t,this.frameStart=0};En.prototype.beginFrame=function(){this.frameStart=Date.now()},En.prototype.allowedToUpload=function(){return Date.now()-this.frameStart0||e.responseType===t.XHR_RESPONSE_TYPE.BUFFER)?r=200:1223===r&&(r=204),2===(r/100|0)){if(this.xhrType===t.XHR_RESPONSE_TYPE.TEXT)this.data=i,this.type=t.TYPE.TEXT;else if(this.xhrType===t.XHR_RESPONSE_TYPE.JSON)try{this.data=JSON.parse(i),this.type=t.TYPE.JSON}catch(t){return void this.abort("Error trying to parse loaded json: "+t)}else if(this.xhrType===t.XHR_RESPONSE_TYPE.DOCUMENT)try{if(window.DOMParser){var n=new DOMParser;this.data=n.parseFromString(i,"text/xml")}else{var o=document.createElement("div");o.innerHTML=i,this.data=o}this.type=t.TYPE.XML}catch(t){return void this.abort("Error trying to parse loaded xml: "+t)}else this.data=e.response||i;this.complete()}else this.abort("["+e.status+"] "+e.statusText+": "+e.responseURL)},e._determineCrossOrigin=function(t,e){if(0===t.indexOf("data:"))return"";if(window.origin!==window.location.origin)return"anonymous";e=e||window.location,jn||(jn=document.createElement("a")),jn.href=t;var i=!(t=On()(jn.href,{strictMode:!0})).port&&""===e.port||t.port===e.port,r=t.protocol?t.protocol+":":"";return t.host===e.hostname&&i&&r===e.protocol?"":"anonymous"},e._determineXhrType=function(){return t._xhrTypeMap[this.extension]||t.XHR_RESPONSE_TYPE.TEXT},e._determineLoadType=function(){return t._loadTypeMap[this.extension]||t.LOAD_TYPE.XHR},e._getExtension=function(){var t=this.url,e="";if(this.isDataUrl){var i=t.indexOf("/");e=t.substring(i+1,t.indexOf(";",i))}else{var r=t.indexOf("?"),n=t.indexOf("#"),o=Math.min(r>-1?r:t.length,n>-1?n:t.length);e=(t=t.substring(0,o)).substring(t.lastIndexOf(".")+1)}return e.toLowerCase()},e._getMimeFromXhrType=function(e){switch(e){case t.XHR_RESPONSE_TYPE.BUFFER:return"application/octet-binary";case t.XHR_RESPONSE_TYPE.BLOB:return"application/blob";case t.XHR_RESPONSE_TYPE.DOCUMENT:return"application/xml";case t.XHR_RESPONSE_TYPE.JSON:return"application/json";case t.XHR_RESPONSE_TYPE.DEFAULT:case t.XHR_RESPONSE_TYPE.TEXT:default:return"text/plain"}},Bn(t,[{key:"isDataUrl",get:function(){return this._hasFlag(t.STATUS_FLAGS.DATA_URL)}},{key:"isComplete",get:function(){return this._hasFlag(t.STATUS_FLAGS.COMPLETE)}},{key:"isLoading",get:function(){return this._hasFlag(t.STATUS_FLAGS.LOADING)}}]),t}();function Xn(t,e,i){e&&0===e.indexOf(".")&&(e=e.substring(1)),e&&(t[e]=i)}function Gn(t){return t.toString().replace("object ","")}zn.STATUS_FLAGS={NONE:0,DATA_URL:1,COMPLETE:2,LOADING:4},zn.TYPE={UNKNOWN:0,JSON:1,XML:2,IMAGE:3,AUDIO:4,VIDEO:5,TEXT:6},zn.LOAD_TYPE={XHR:1,IMAGE:2,AUDIO:3,VIDEO:4},zn.XHR_RESPONSE_TYPE={DEFAULT:"text",BUFFER:"arraybuffer",BLOB:"blob",DOCUMENT:"document",JSON:"json",TEXT:"text"},zn._loadTypeMap={gif:zn.LOAD_TYPE.IMAGE,png:zn.LOAD_TYPE.IMAGE,bmp:zn.LOAD_TYPE.IMAGE,jpg:zn.LOAD_TYPE.IMAGE,jpeg:zn.LOAD_TYPE.IMAGE,tif:zn.LOAD_TYPE.IMAGE,tiff:zn.LOAD_TYPE.IMAGE,webp:zn.LOAD_TYPE.IMAGE,tga:zn.LOAD_TYPE.IMAGE,svg:zn.LOAD_TYPE.IMAGE,"svg+xml":zn.LOAD_TYPE.IMAGE,mp3:zn.LOAD_TYPE.AUDIO,ogg:zn.LOAD_TYPE.AUDIO,wav:zn.LOAD_TYPE.AUDIO,mp4:zn.LOAD_TYPE.VIDEO,webm:zn.LOAD_TYPE.VIDEO},zn._xhrTypeMap={xhtml:zn.XHR_RESPONSE_TYPE.DOCUMENT,html:zn.XHR_RESPONSE_TYPE.DOCUMENT,htm:zn.XHR_RESPONSE_TYPE.DOCUMENT,xml:zn.XHR_RESPONSE_TYPE.DOCUMENT,tmx:zn.XHR_RESPONSE_TYPE.DOCUMENT,svg:zn.XHR_RESPONSE_TYPE.DOCUMENT,tsx:zn.XHR_RESPONSE_TYPE.DOCUMENT,gif:zn.XHR_RESPONSE_TYPE.BLOB,png:zn.XHR_RESPONSE_TYPE.BLOB,bmp:zn.XHR_RESPONSE_TYPE.BLOB,jpg:zn.XHR_RESPONSE_TYPE.BLOB,jpeg:zn.XHR_RESPONSE_TYPE.BLOB,tif:zn.XHR_RESPONSE_TYPE.BLOB,tiff:zn.XHR_RESPONSE_TYPE.BLOB,webp:zn.XHR_RESPONSE_TYPE.BLOB,tga:zn.XHR_RESPONSE_TYPE.BLOB,json:zn.XHR_RESPONSE_TYPE.JSON,text:zn.XHR_RESPONSE_TYPE.TEXT,txt:zn.XHR_RESPONSE_TYPE.TEXT,ttf:zn.XHR_RESPONSE_TYPE.BUFFER,otf:zn.XHR_RESPONSE_TYPE.BUFFER},zn.EMPTY_GIF="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";var Yn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var Vn=window.URL||window.webkitURL;var Wn={caching:function(t,e){var i=this;Ln[t.url]?(t.data=Ln[t.url],t.complete()):t.onComplete.once(function(){return Ln[i.url]=i.data}),e()},parsing:function(t,e){if(t.data){if(t.xhr&&t.xhrType===zn.XHR_RESPONSE_TYPE.BLOB)if(window.Blob&&"string"!=typeof t.data){if(0===t.data.type.indexOf("image")){var i=Vn.createObjectURL(t.data);return t.blob=t.data,t.data=new Image,t.data.src=i,t.type=zn.TYPE.IMAGE,void(t.data.onload=function(){Vn.revokeObjectURL(i),t.data.onload=null,e()})}}else{var r=t.xhr.getResponseHeader("content-type");if(r&&0===r.indexOf("image"))return t.data=new Image,t.data.src="data:"+r+";base64,"+function(t){for(var e="",i=0;i>2,n[1]=(3&r[0])<<4|r[1]>>4,n[2]=(15&r[1])<<2|r[2]>>6,n[3]=63&r[2],i-(t.length-1)){case 2:n[3]=64,n[2]=64;break;case 1:n[3]=64}for(var s=0;s16384&&(r=16384),this._properties=[!1,!0,!1,!1,!1],this._maxSize=e,this._batchSize=r,this._buffers=null,this._bufferUpdateIDs=[],this._updateID=0,this.interactiveChildren=!1,this.blendMode=b.NORMAL,this.autoResize=n,this.roundPixels=!0,this.baseTexture=null,this.setProperties(i),this._tint=0,this.tintRgb=new Float32Array(4),this.tint=16777215}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={tint:{configurable:!0}};return e.prototype.setProperties=function(t){t&&(this._properties[0]="vertices"in t||"scale"in t?!!t.vertices||!!t.scale:this._properties[0],this._properties[1]="position"in t?!!t.position:this._properties[1],this._properties[2]="rotation"in t?!!t.rotation:this._properties[2],this._properties[3]="uvs"in t?!!t.uvs:this._properties[3],this._properties[4]="tint"in t||"alpha"in t?!!t.tint||!!t.alpha:this._properties[4])},e.prototype.updateTransform=function(){this.displayObjectUpdateTransform()},i.tint.get=function(){return this._tint},i.tint.set=function(t){this._tint=t,U(t,this.tintRgb)},e.prototype.render=function(t){var e=this;this.visible&&!(this.worldAlpha<=0)&&this.children.length&&this.renderable&&(this.baseTexture||(this.baseTexture=this.children[0]._texture.baseTexture,this.baseTexture.valid||this.baseTexture.once("update",function(){return e.onChildrenChange(0)})),t.batch.setObjectRenderer(t.plugins.particle),t.plugins.particle.render(this))},e.prototype.onChildrenChange=function(t){for(var e=Math.floor(t/this._batchSize);this._bufferUpdateIDs.lengthi&&!t.autoResize&&(o=i);var s=t._buffers;s||(s=t._buffers=this.generateBuffers(t));var a=e[0]._texture.baseTexture;this.renderer.state.setBlendMode(G(t.blendMode,a.premultiplyAlpha));var h=n.gl,u=t.worldTransform.copyTo(this.tempMatrix);u.prepend(n.globalUniforms.uniforms.projectionMatrix),this.shader.uniforms.translationMatrix=u.toArray(!0),this.shader.uniforms.uColor=Y(t.tintRgb,t.worldAlpha,this.shader.uniforms.uColor,a.premultiplyAlpha),this.shader.uniforms.uSampler=a,this.renderer.shader.bind(this.shader);for(var l=!1,c=0,d=0;cr&&(p=r),d>=s.length&&s.push(this._generateOneMoreBuffer(t));var f=s[d];f.uploadDynamic(e,c,p);var g=t._bufferUpdateIDs[d]||0;(l=l||f._updateID=r&&ho.x=n&&ho.y0&&r.x>h&&(J(n,1+p-++g,1+y-p),y=p,p=-1,o.push(f),c=Math.max(c,f),d++,r.x=0,r.y+=t.lineHeight,u=null))}else o.push(l),c=Math.max(c,l),++d,++g,r.x=0,r.y+=t.lineHeight,u=null}var b=s.charAt(s.length-1);"\r"!==b&&"\n"!==b&&(/(?:\s)/.test(b)&&(l=f),o.push(l),c=Math.max(c,l));for(var w=[],T=0;T<=d;T++){var E=0;"right"===this._font.align?E=c-o[T]:"center"===this._font.align&&(E=(c-o[T])/2),w.push(E)}for(var S=n.length,P=this.tint,A=0;A=0?t:16777215,this.dirty=!0},i.align.get=function(){return this._font.align},i.align.set=function(t){this._font.align=t||"left",this.dirty=!0},i.anchor.get=function(){return this._anchor},i.anchor.set=function(t){"number"==typeof t?this._anchor.set(t):this._anchor.copyFrom(t)},i.font.get=function(){return this._font},i.font.set=function(t){t&&("string"==typeof t?(t=t.split(" "),this._font.name=1===t.length?t[0]:t.slice(1).join(" "),this._font.size=t.length>=2?parseInt(t[0],10):e.fonts[this._font.name].size):(this._font.name=t.name,this._font.size="number"==typeof t.size?t.size:parseInt(t.size,10)),this.dirty=!0)},i.text.get=function(){return this._text},i.text.set=function(t){t=String(null==t?"":t),this._text!==t&&(this._text=t,this.dirty=!0)},i.maxWidth.get=function(){return this._maxWidth},i.maxWidth.set=function(t){this._maxWidth!==t&&(this._maxWidth=t,this.dirty=!0)},i.maxLineHeight.get=function(){return this.validate(),this._maxLineHeight},i.textWidth.get=function(){return this.validate(),this._textWidth},i.letterSpacing.get=function(){return this._letterSpacing},i.letterSpacing.set=function(t){this._letterSpacing!==t&&(this._letterSpacing=t,this.dirty=!0)},i.textHeight.get=function(){return this.validate(),this._textHeight},e.registerFont=function(t,i){var r={},n=t.getElementsByTagName("info")[0],o=t.getElementsByTagName("common")[0],s=t.getElementsByTagName("page"),a=ft(s[0].getAttribute("file"),d.RESOLUTION),h={};r.font=n.getAttribute("face"),r.size=parseInt(n.getAttribute("size"),10),r.lineHeight=parseInt(o.getAttribute("lineHeight"),10)/a,r.chars={},i instanceof Ie&&(i=[i]);for(var u=0;u>16&255)/255,s=(i>>8&255)/255,a=(255&i)/255,h=((r=r||3375104)>>16&255)/255,u=(r>>8&255)/255,l=(255&r)/255,c=[.3,.59,.11,0,0,o,s,a,t=t||.2,0,h,u,l,e=e||.15,0,o-h,s-u,a-l,0,0];this._loadMatrix(c,n)},e.prototype.night=function(t,e){var i=[-2*(t=t||.1),-t,0,0,0,-t,0,t,0,0,0,t,2*t,0,0,0,0,0,1,0];this._loadMatrix(i,e)},e.prototype.predator=function(t,e){var i=[11.224130630493164*t,-4.794486999511719*t,-2.8746118545532227*t,0*t,.40342438220977783*t,-3.6330697536468506*t,9.193157196044922*t,-2.951810836791992*t,0*t,-1.316135048866272*t,-3.2184197902679443*t,-4.2375030517578125*t,7.476448059082031*t,0*t,.8044459223747253*t,0,0,0,1,0];this._loadMatrix(i,e)},e.prototype.lsd=function(t){this._loadMatrix([2,-.4,.5,0,0,-.5,2,-.4,0,0,-.4,-.5,3,0,0,0,0,0,1,0],t)},e.prototype.reset=function(){this._loadMatrix([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],!1)},i.matrix.get=function(){return this.uniforms.m},i.matrix.set=function(t){this.uniforms.m=t},i.alpha.get=function(){return this.uniforms.uAlpha},i.alpha.set=function(t){this.uniforms.uAlpha=t},Object.defineProperties(e.prototype,i),e}(Ii);mo.prototype.grayscale=mo.prototype.greyscale; +/*! + * @pixi/filter-displacement - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/filter-displacement is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +var xo=new Et;Gt.prototype._cacheAsBitmap=!1,Gt.prototype._cacheData=!1;var bo=function(){this.textureCacheId=null,this.originalRender=null,this.originalRenderCanvas=null,this.originalCalculateBounds=null,this.originalGetLocalBounds=null,this.originalUpdateTransform=null,this.originalHitTest=null,this.originalDestroy=null,this.originalMask=null,this.originalFilterArea=null,this.sprite=null};Object.defineProperties(Gt.prototype,{cacheAsBitmap:{get:function(){return this._cacheAsBitmap},set:function(t){var e;this._cacheAsBitmap!==t&&(this._cacheAsBitmap=t,t?(this._cacheData||(this._cacheData=new bo),(e=this._cacheData).originalRender=this.render,e.originalRenderCanvas=this.renderCanvas,e.originalUpdateTransform=this.updateTransform,e.originalCalculateBounds=this.calculateBounds,e.originalGetLocalBounds=this.getLocalBounds,e.originalDestroy=this.destroy,e.originalContainsPoint=this.containsPoint,e.originalMask=this._mask,e.originalFilterArea=this.filterArea,this.render=this._renderCached,this.renderCanvas=this._renderCachedCanvas,this.destroy=this._cacheAsBitmapDestroy):((e=this._cacheData).sprite&&this._destroyCachedDisplayObject(),this.render=e.originalRender,this.renderCanvas=e.originalRenderCanvas,this.calculateBounds=e.originalCalculateBounds,this.getLocalBounds=e.originalGetLocalBounds,this.destroy=e.originalDestroy,this.updateTransform=e.originalUpdateTransform,this.containsPoint=e.originalContainsPoint,this._mask=e.originalMask,this.filterArea=e.originalFilterArea))}}}),Gt.prototype._renderCached=function(t){!this.visible||this.worldAlpha<=0||!this.renderable||(this._initCachedDisplayObject(t),this._cacheData.sprite.transform._worldID=this.transform._worldID,this._cacheData.sprite.worldAlpha=this.worldAlpha,this._cacheData.sprite._render(t))},Gt.prototype._initCachedDisplayObject=function(t){if(!this._cacheData||!this._cacheData.sprite){var e=this.alpha;this.alpha=1,t.batch.flush();var i=this.getLocalBounds().clone();if(this.filters){var r=this.filters[0].padding;i.pad(r)}i.ceil(d.RESOLUTION);var n=t.renderTexture.current,o=t.renderTexture.sourceFrame,s=t.projection.transform,a=De.create(i.width,i.height),h="cacheAsBitmap_"+K();this._cacheData.textureCacheId=h,ge.addToCache(a.baseTexture,h),Ie.addToCache(a,h);var u=xo;u.tx=-i.x,u.ty=-i.y,this.transform.worldTransform.identity(),this.render=this._cacheData.originalRender,t.render(this,a,!0,u,!0),t.projection.transform=s,t.renderTexture.bind(n,o),this.render=this._renderCached,this.updateTransform=this.displayObjectUpdateTransform,this.calculateBounds=this._calculateCachedBounds,this.getLocalBounds=this._getCachedLocalBounds,this._mask=null,this.filterArea=null;var l=new Kr(a);l.transform.worldTransform=this.transform.worldTransform,l.anchor.x=-i.x/i.width,l.anchor.y=-i.y/i.height,l.alpha=e,l._bounds=this._bounds,this._cacheData.sprite=l,this.transform._parentID=-1,this.parent?this.updateTransform():(this.parent=t._tempDisplayObjectParent,this.updateTransform(),this.parent=null),this.containsPoint=l.containsPoint.bind(l)}},Gt.prototype._renderCachedCanvas=function(t){!this.visible||this.worldAlpha<=0||!this.renderable||(this._initCachedDisplayObjectCanvas(t),this._cacheData.sprite.worldAlpha=this.worldAlpha,this._cacheData.sprite._renderCanvas(t))},Gt.prototype._initCachedDisplayObjectCanvas=function(t){if(!this._cacheData||!this._cacheData.sprite){var e=this.getLocalBounds(),i=this.alpha;this.alpha=1;var r=t.context;e.ceil(d.RESOLUTION);var n=De.create(e.width,e.height),o="cacheAsBitmap_"+K();this._cacheData.textureCacheId=o,ge.addToCache(n.baseTexture,o),Ie.addToCache(n,o);var s=xo;this.transform.localTransform.copyTo(s),s.invert(),s.tx-=e.x,s.ty-=e.y,this.renderCanvas=this._cacheData.originalRenderCanvas,t.render(this,n,!0,s,!1),t.context=r,this.renderCanvas=this._renderCachedCanvas,this.updateTransform=this.displayObjectUpdateTransform,this.calculateBounds=this._calculateCachedBounds,this.getLocalBounds=this._getCachedLocalBounds,this._mask=null,this.filterArea=null;var a=new Kr(n);a.transform.worldTransform=this.transform.worldTransform,a.anchor.x=-e.x/e.width,a.anchor.y=-e.y/e.height,a.alpha=i,a._bounds=this._bounds,this._cacheData.sprite=a,this.transform._parentID=-1,this.parent?this.updateTransform():(this.parent=t._tempDisplayObjectParent,this.updateTransform(),this.parent=null),this.containsPoint=a.containsPoint.bind(a)}},Gt.prototype._calculateCachedBounds=function(){this._bounds.clear(),this._cacheData.sprite.transform._worldID=this.transform._worldID,this._cacheData.sprite._calculateBounds(),this._lastBoundsID=this._boundsID},Gt.prototype._getCachedLocalBounds=function(){return this._cacheData.sprite.getLocalBounds()},Gt.prototype._destroyCachedDisplayObject=function(){this._cacheData.sprite._texture.destroy(!0),this._cacheData.sprite=null,ge.removeFromCache(this._cacheData.textureCacheId),Ie.removeFromCache(this._cacheData.textureCacheId),this._cacheData.textureCacheId=null},Gt.prototype._cacheAsBitmapDestroy=function(t){this.cacheAsBitmap=!1,this.destroy(t)}, +/*! + * @pixi/mixin-get-child-by-name - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/mixin-get-child-by-name is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +Gt.prototype.name=null,Vt.prototype.getChildByName=function(t){for(var e=0;e>16)+(65280&t)+((255&t)<<16),this._colorDirty=!0)},i.tint.get=function(){return this._tint},e.prototype.update=function(){if(this._colorDirty){this._colorDirty=!1;var t=this.texture.baseTexture;W(this._tint,this._alpha,this.uniforms.uColor,t.premultiplyAlpha)}this.uvMatrix.update()&&(this.uniforms.uTextureMatrix=this.uvMatrix.mapCoord)},Object.defineProperties(e.prototype,i),e}(Pi),Oo=function(t){function e(e,i,r){t.call(this);var n=new Le(e),o=new Le(i,!0),s=new Le(r,!0,!0);this.addAttribute("aVertexPosition",n,2,!1,S.FLOAT).addAttribute("aTextureCoord",o,2,!1,S.FLOAT).addIndex(s),this._updateId=-1}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={vertexDirtyId:{configurable:!0}};return i.vertexDirtyId.get=function(){return this.buffers[0]._updateID},Object.defineProperties(e.prototype,i),e}(ze),Io=function(t){function e(e,i,r,n){void 0===e&&(e=100),void 0===i&&(i=100),void 0===r&&(r=10),void 0===n&&(n=10),t.call(this),this.segWidth=r,this.segHeight=n,this.width=e,this.height=i,this.build()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.build=function(){for(var t=this.segWidth*this.segHeight,e=[],i=[],r=[],n=this.segWidth-1,o=this.segHeight-1,s=this.width/n,a=this.height/o,h=0;he?1:this._height/e;t[9]=t[11]=t[13]=t[15]=this._topHeight*i,t[17]=t[19]=t[21]=t[23]=this._height-this._bottomHeight*i,t[25]=t[27]=t[29]=t[31]=this._height},e.prototype.updateVerticalVertices=function(){var t=this.vertices,e=this._leftWidth+this._rightWidth,i=this._width>e?1:this._width/e;t[2]=t[10]=t[18]=t[26]=this._leftWidth*i,t[4]=t[12]=t[20]=t[28]=this._width-this._rightWidth*i,t[6]=t[14]=t[22]=t[30]=this._width},i.width.get=function(){return this._width},i.width.set=function(t){this._width=t,this._refresh()},i.height.get=function(){return this._height},i.height.set=function(t){this._height=t,this._refresh()},i.leftWidth.get=function(){return this._leftWidth},i.leftWidth.set=function(t){this._leftWidth=t,this._refresh()},i.rightWidth.get=function(){return this._rightWidth},i.rightWidth.set=function(t){this._rightWidth=t,this._refresh()},i.topHeight.get=function(){return this._topHeight},i.topHeight.set=function(t){this._topHeight=t,this._refresh()},i.bottomHeight.get=function(){return this._bottomHeight},i.bottomHeight.set=function(t){this._bottomHeight=t,this._refresh()},e.prototype._refresh=function(){var t=this.texture,e=this.geometry.buffers[1].data;this._origWidth=t.orig.width,this._origHeight=t.orig.height;var i=1/this._origWidth,r=1/this._origHeight;e[0]=e[8]=e[16]=e[24]=0,e[1]=e[3]=e[5]=e[7]=0,e[6]=e[14]=e[22]=e[30]=1,e[25]=e[27]=e[29]=e[31]=1,e[2]=e[10]=e[18]=e[26]=i*this._leftWidth,e[4]=e[12]=e[20]=e[28]=1-i*this._rightWidth,e[9]=e[11]=e[13]=e[15]=r*this._topHeight,e[17]=e[19]=e[21]=e[23]=1-r*this._bottomHeight,this.updateHorizontalVertices(),this.updateVerticalVertices(),this.geometry.buffers[0].update(),this.geometry.buffers[1].update()},Object.defineProperties(e.prototype,i),e}(Ro),No=function(t){function e(e,i){t.call(this,e[0]instanceof Ie?e[0]:e[0].texture),this._textures=null,this._durations=null,this.textures=e,this._autoUpdate=!1!==i,this.animationSpeed=1,this.loop=!0,this.updateAnchor=!1,this.onComplete=null,this.onFrameChange=null,this.onLoop=null,this._currentTime=0,this.playing=!1}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var i={totalFrames:{configurable:!0},textures:{configurable:!0},currentFrame:{configurable:!0}};return e.prototype.stop=function(){this.playing&&(this.playing=!1,this._autoUpdate&&re.shared.remove(this.update,this))},e.prototype.play=function(){this.playing||(this.playing=!0,this._autoUpdate&&re.shared.add(this.update,this,Qt))},e.prototype.gotoAndStop=function(t){this.stop();var e=this.currentFrame;this._currentTime=t,e!==this.currentFrame&&this.updateTexture()},e.prototype.gotoAndPlay=function(t){var e=this.currentFrame;this._currentTime=t,e!==this.currentFrame&&this.updateTexture(),this.play()},e.prototype.update=function(t){var e=this.animationSpeed*t,i=this.currentFrame;if(null!==this._durations){var r=this._currentTime%1*this._durations[this.currentFrame];for(r+=e/60*1e3;r<0;)this._currentTime--,r+=this._durations[this.currentFrame];var n=Math.sign(this.animationSpeed*t);for(this._currentTime=Math.floor(this._currentTime);r>=this._durations[this.currentFrame];)r-=this._durations[this.currentFrame]*n,this._currentTime+=n;this._currentTime+=r/this._durations[this.currentFrame]}else this._currentTime+=e;this._currentTime<0&&!this.loop?(this.gotoAndStop(0),this.onComplete&&this.onComplete()):this._currentTime>=this._textures.length&&!this.loop?(this.gotoAndStop(this._textures.length-1),this.onComplete&&this.onComplete()):i!==this.currentFrame&&(this.loop&&this.onLoop&&(this.animationSpeed>0&&this.currentFramei&&this.onLoop()),this.updateTexture())},e.prototype.updateTexture=function(){this._texture=this._textures[this.currentFrame],this._textureID=-1,this._textureTrimmedID=-1,this._cachedTint=16777215,this.uvs=this._texture._uvs.uvsFloat32,this.updateAnchor&&this._anchor.copy(this._texture.defaultAnchor),this.onFrameChange&&this.onFrameChange(this.currentFrame)},e.prototype.destroy=function(e){this.stop(),t.prototype.destroy.call(this,e),this.onComplete=null,this.onFrameChange=null,this.onLoop=null},e.fromFrames=function(t){for(var i=[],r=0;rE?E:T,e.moveTo(m,x+T),e.lineTo(m,x+w-T),e.quadraticCurveTo(m,x+w,m+T,x+w),e.lineTo(m+b-T,x+w),e.quadraticCurveTo(m+b,x+w,m+b,x+w-T),e.lineTo(m+b,x+T),e.quadraticCurveTo(m+b,x,m+b-T,x),e.lineTo(m+T,x),e.quadraticCurveTo(m,x,m,x+T),e.closePath()}}}},Bo.prototype.popMask=function(t){t.context.restore(),t.invalidateBlendMode()},Bo.prototype.destroy=function(){};var Ho=function(t){function e(i,r,n){var o;t.call(this,"Canvas",i,r,n),this.type=x.CANVAS,this.rootContext=this.view.getContext("2d",{alpha:this.transparent}),this.context=this.rootContext,this.refresh=!0,this.maskManager=new Bo(this),this.smoothProperty="imageSmoothingEnabled",this.rootContext.imageSmoothingEnabled||(this.rootContext.webkitImageSmoothingEnabled?this.smoothProperty="webkitImageSmoothingEnabled":this.rootContext.mozImageSmoothingEnabled?this.smoothProperty="mozImageSmoothingEnabled":this.rootContext.oImageSmoothingEnabled?this.smoothProperty="oImageSmoothingEnabled":this.rootContext.msImageSmoothingEnabled&&(this.smoothProperty="msImageSmoothingEnabled")),this.initPlugins(e.__plugins),this.blendModes=(void 0===o&&(o=[]),jo()?(o[b.NORMAL]="source-over",o[b.ADD]="lighter",o[b.MULTIPLY]="multiply",o[b.SCREEN]="screen",o[b.OVERLAY]="overlay",o[b.DARKEN]="darken",o[b.LIGHTEN]="lighten",o[b.COLOR_DODGE]="color-dodge",o[b.COLOR_BURN]="color-burn",o[b.HARD_LIGHT]="hard-light",o[b.SOFT_LIGHT]="soft-light",o[b.DIFFERENCE]="difference",o[b.EXCLUSION]="exclusion",o[b.HUE]="hue",o[b.SATURATION]="saturate",o[b.COLOR]="color",o[b.LUMINOSITY]="luminosity"):(o[b.NORMAL]="source-over",o[b.ADD]="lighter",o[b.MULTIPLY]="source-over",o[b.SCREEN]="source-over",o[b.OVERLAY]="source-over",o[b.DARKEN]="source-over",o[b.LIGHTEN]="source-over",o[b.COLOR_DODGE]="source-over",o[b.COLOR_BURN]="source-over",o[b.HARD_LIGHT]="source-over",o[b.SOFT_LIGHT]="source-over",o[b.DIFFERENCE]="source-over",o[b.EXCLUSION]="source-over",o[b.HUE]="source-over",o[b.SATURATION]="source-over",o[b.COLOR]="source-over",o[b.LUMINOSITY]="source-over"),o[b.NORMAL_NPM]=o[b.NORMAL],o[b.ADD_NPM]=o[b.ADD],o[b.SCREEN_NPM]=o[b.SCREEN],o[b.SRC_IN]="source-in",o[b.SRC_OUT]="source-out",o[b.SRC_ATOP]="source-atop",o[b.DST_OVER]="destination-over",o[b.DST_IN]="destination-in",o[b.DST_OUT]="destination-out",o[b.DST_ATOP]="destination-atop",o[b.SUBTRACT]="source-over",o),this._activeBlendMode=null,this._outerBlend=!1,this.renderingToScreen=!1,N("Canvas"),this.resize(this.options.width,this.options.height)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.render=function(t,e,i,r,n){if(this.view){this.renderingToScreen=!e,this.emit("prerender");var o=this.resolution;e?((e=e.baseTexture||e)._canvasRenderTarget||(e._canvasRenderTarget=new ht(e.width,e.height,e.resolution),e.resource=new we.CanvasResource(e._canvasRenderTarget.canvas),e.valid=!0),this.context=e._canvasRenderTarget.context,this.resolution=e._canvasRenderTarget.resolution):this.context=this.rootContext;var s=this.context;if(e||(this._lastObjectRendered=t),!n){var a=t.parent,h=this._tempDisplayObjectParent.transform.worldTransform;r?(r.copyTo(h),this._tempDisplayObjectParent.transform._worldID=-1):h.identity(),t.parent=this._tempDisplayObjectParent,t.updateTransform(),t.parent=a}s.save(),s.setTransform(1,0,0,1,0,0),s.globalAlpha=1,this._activeBlendMode=b.NORMAL,this._outerBlend=!1,s.globalCompositeOperation=this.blendModes[b.NORMAL],(void 0!==i?i:this.clearBeforeRender)&&this.renderingToScreen&&(this.transparent?s.clearRect(0,0,this.width,this.height):(s.fillStyle=this._backgroundColorString,s.fillRect(0,0,this.width,this.height)));var u=this.context;this.context=s,t.renderCanvas(this),this.context=u,s.restore(),this.resolution=o,this.emit("postrender")}},e.prototype.clear=function(t){var e=this.context;t=t||this._backgroundColorString,!this.transparent&&t?(e.fillStyle=t,e.fillRect(0,0,this.width,this.height)):e.clearRect(0,0,this.width,this.height)},e.prototype.setBlendMode=function(t,e){var i=t===b.SRC_IN||t===b.SRC_OUT||t===b.DST_IN||t===b.DST_ATOP;!e&&i&&(t=b.NORMAL),this._activeBlendMode!==t&&(this._activeBlendMode=t,this._outerBlend=i,this.context.globalCompositeOperation=this.blendModes[t])},e.prototype.destroy=function(e){t.prototype.destroy.call(this,e),this.context=null,this.refresh=!0,this.maskManager.destroy(),this.maskManager=null,this.smoothProperty=null},e.prototype.resize=function(e,i){t.prototype.resize.call(this,e,i),this.smoothProperty&&(this.rootContext[this.smoothProperty]=d.SCALE_MODE===P)},e.prototype.invalidateBlendMode=function(){this._activeBlendMode=this.blendModes.indexOf(this.context.globalCompositeOperation)},e.registerPlugin=function(t,i){e.__plugins=e.__plugins||{},e.__plugins[t]=i},e}(er),zo={getTintedCanvas:function(t,e){var i=t.texture,r="#"+("00000"+(0|(e=zo.roundColor(e))).toString(16)).substr(-6);i.tintCache=i.tintCache||{};var n,o=i.tintCache[r];if(o){if(o.tintId===i._updateID)return i.tintCache[r];n=i.tintCache[r]}else n=zo.canvas||document.createElement("canvas");if(zo.tintMethod(i,e,n),n.tintId=i._updateID,zo.convertTintToImage){var s=new Image;s.src=n.toDataURL(),i.tintCache[r]=s}else i.tintCache[r]=n,zo.canvas=null;return n},tintWithMultiply:function(t,e,i){var r=i.getContext("2d"),n=t._frame.clone(),o=t.baseTexture.resolution;n.x*=o,n.y*=o,n.width*=o,n.height*=o,i.width=Math.ceil(n.width),i.height=Math.ceil(n.height),r.save(),r.fillStyle="#"+("00000"+(0|e).toString(16)).substr(-6),r.fillRect(0,0,n.width,n.height),r.globalCompositeOperation="multiply";var s=t.baseTexture.getDrawableSource();r.drawImage(s,n.x,n.y,n.width,n.height,0,0,n.width,n.height),r.globalCompositeOperation="destination-atop",r.drawImage(s,n.x,n.y,n.width,n.height,0,0,n.width,n.height),r.restore()},tintWithOverlay:function(t,e,i){var r=i.getContext("2d"),n=t._frame.clone(),o=t.baseTexture.resolution;n.x*=o,n.y*=o,n.width*=o,n.height*=o,i.width=Math.ceil(n.width),i.height=Math.ceil(n.height),r.save(),r.globalCompositeOperation="copy",r.fillStyle="#"+("00000"+(0|e).toString(16)).substr(-6),r.fillRect(0,0,n.width,n.height),r.globalCompositeOperation="destination-atop",r.drawImage(t.baseTexture.getDrawableSource(),n.x,n.y,n.width,n.height,0,0,n.width,n.height),r.restore()},tintWithPerPixel:function(t,e,i){var r=i.getContext("2d"),n=t._frame.clone(),o=t.baseTexture.resolution;n.x*=o,n.y*=o,n.width*=o,n.height*=o,i.width=Math.ceil(n.width),i.height=Math.ceil(n.height),r.save(),r.globalCompositeOperation="copy",r.drawImage(t.baseTexture.getDrawableSource(),n.x,n.y,n.width,n.height,0,0,n.width,n.height),r.restore();for(var s=U(e),a=s[0],h=s[1],u=s[2],l=r.getImageData(0,0,n.width,n.height),c=l.data,d=0;d0){var S=E/Math.abs(t.worldTransform.a),P=E/Math.abs(t.worldTransform.d),A=(_+m+x)/3,C=(b+w+T)/3,O=_-A,I=b-C,M=Math.sqrt(O*O+I*I);_=A+O/M*(M+S),b=C+I/M*(M+P),I=w-C,m=A+(O=m-A)/(M=Math.sqrt(O*O+I*I))*(M+S),w=C+I/M*(M+P),I=T-C,x=A+(O=x-A)/(M=Math.sqrt(O*O+I*I))*(M+S),T=C+I/M*(M+P)}n.save(),n.beginPath(),n.moveTo(_,b),n.lineTo(m,w),n.lineTo(x,T),n.closePath(),n.clip();var D=d*v+g*f+p*y-v*f-g*p-d*y,R=_*v+g*x+m*y-v*x-g*m-_*y,k=d*m+_*f+p*x-m*f-_*p-d*x,F=d*v*x+g*m*f+_*p*y-_*v*f-g*p*x-d*m*y,L=b*v+g*T+w*y-v*T-g*w-b*y,N=d*w+b*f+p*T-w*f-b*p-d*T,B=d*v*T+g*w*f+b*p*y-b*v*f-g*p*T-d*w*y;n.transform(R/D,L/D,k/D,N/D,F/D,B/D),n.drawImage(u,0,0,l*h.resolution,c*h.resolution,0,0,l,c),n.restore(),this.renderer.invalidateBlendMode()}},Go.prototype.renderMeshFlat=function(t){var e=this.renderer.context,i=t.geometry.getBuffer("aVertexPosition").data,r=i.length/2;e.beginPath();for(var n=1;n0){v=0,_=f[0],m=f[1];for(var b=2;b+2=0;S-=2)i.lineTo(f[S],f[S+1])}g[w].shape.closeStroke&&i.closePath()}}l.visible&&(i.globalAlpha=l.alpha*r,i.fillStyle="#"+("00000"+(0|d).toString(16)).substr(-6),i.fill()),c.visible&&(i.globalAlpha=c.alpha*r,i.strokeStyle="#"+("00000"+(0|p).toString(16)).substr(-6),i.stroke())}else if(h.type===Tt.RECT)l.visible&&(i.globalAlpha=l.alpha*r,i.fillStyle="#"+("00000"+(0|d).toString(16)).substr(-6),i.fillRect(u.x,u.y,u.width,u.height)),c.visible&&(i.globalAlpha=c.alpha*r,i.strokeStyle="#"+("00000"+(0|p).toString(16)).substr(-6),i.strokeRect(u.x,u.y,u.width,u.height));else if(h.type===Tt.CIRC)i.beginPath(),i.arc(u.x,u.y,u.radius,0,2*Math.PI),i.closePath(),l.visible&&(i.globalAlpha=l.alpha*r,i.fillStyle="#"+("00000"+(0|d).toString(16)).substr(-6),i.fill()),c.visible&&(i.globalAlpha=c.alpha*r,i.strokeStyle="#"+("00000"+(0|p).toString(16)).substr(-6),i.stroke());else if(h.type===Tt.ELIP){var P=2*u.width,A=2*u.height,C=u.x-P/2,O=u.y-A/2;i.beginPath();var I=P/2*.5522848,M=A/2*.5522848,D=C+P,R=O+A,k=C+P/2,F=O+A/2;i.moveTo(C,F),i.bezierCurveTo(C,F-M,k-I,O,k,O),i.bezierCurveTo(k+I,O,D,F-M,D,F),i.bezierCurveTo(D,F+M,k+I,R,k,R),i.bezierCurveTo(k-I,R,C,F+M,C,F),i.closePath(),l.visible&&(i.globalAlpha=l.alpha*r,i.fillStyle="#"+("00000"+(0|d).toString(16)).substr(-6),i.fill()),c.visible&&(i.globalAlpha=c.alpha*r,i.strokeStyle="#"+("00000"+(0|p).toString(16)).substr(-6),i.stroke())}else if(h.type===Tt.RREC){var L=u.x,N=u.y,B=u.width,U=u.height,j=u.radius,H=Math.min(B,U)/2|0;j=j>H?H:j,i.beginPath(),i.moveTo(L,N+j),i.lineTo(L,N+U-j),i.quadraticCurveTo(L,N+U,L+j,N+U),i.lineTo(L+B-j,N+U),i.quadraticCurveTo(L+B,N+U,L+B,N+U-j),i.lineTo(L+B,N+j),i.quadraticCurveTo(L+B,N,L+B-j,N),i.lineTo(L+j,N),i.quadraticCurveTo(L,N,L,N+j),i.closePath(),l.visible&&(i.globalAlpha=l.alpha*r,i.fillStyle="#"+("00000"+(0|d).toString(16)).substr(-6),i.fill()),c.visible&&(i.globalAlpha=c.alpha*r,i.strokeStyle="#"+("00000"+(0|p).toString(16)).substr(-6),i.stroke())}}},Vo.prototype.updateGraphicsTint=function(t){t._prevTint=t.tint,t.canvasTintDirty=t.geometry.dirty;for(var e=(t.tint>>16&255)/255,i=(t.tint>>8&255)/255,r=(255&t.tint)/255,n=t.geometry.graphicsData,o=0;o>16&255)/255*e*255<<16)+((a>>8&255)/255*i*255<<8)+(255&a)/255*r*255,s._lineTint=((h>>16&255)/255*e*255<<16)+((h>>8&255)/255*i*255<<8)+(255&h)/255*r*255}},Vo.prototype.destroy=function(){this.renderer=null};var Wo=new Et;qr.prototype.generateCanvasTexture=function(t,e){void 0===e&&(e=1);var i=this.getLocalBounds(),r=De.create(i.width,i.height,t,e);Yo||(Yo=new Ho),this.transform.updateLocalTransform(),this.transform.localTransform.copyTo(Wo),Wo.invert(),Wo.tx-=i.x,Wo.ty-=i.y,Yo.render(this,r,!0,Wo);var n=Ie.from(r.baseTexture._canvasRenderTarget.canvas,{scaleMode:t});return n.baseTexture.resolution=e,n.baseTexture.update(),n},qr.prototype.cachedGraphicsData=[],qr.prototype._renderCanvas=function(t){!0!==this.isMask&&(this.finishPoly(),t.plugins.graphics.render(this))}; +/*! + * @pixi/canvas-sprite - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/canvas-sprite is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +var qo=new Et,Jo=function(t){this.renderer=t};Jo.prototype.render=function(t){var e=t._texture,i=this.renderer,r=i.context,n=e._frame.width,o=e._frame.height,s=t.transform.worldTransform,a=0,h=0,u=e.baseTexture.getDrawableSource();if(!(e.orig.width<=0||e.orig.height<=0)&&u&&e.valid){i.setBlendMode(t.blendMode,!0),i.context.globalAlpha=t.worldAlpha;var l=e.baseTexture.scaleMode===P;i.smoothProperty&&i.context[i.smoothProperty]!==l&&(r[i.smoothProperty]=l),e.trim?(a=e.trim.width/2+e.trim.x-t.anchor.x*e.orig.width,h=e.trim.height/2+e.trim.y-t.anchor.y*e.orig.height):(a=(.5-t.anchor.x)*e.orig.width,h=(.5-t.anchor.y)*e.orig.height),e.rotate&&(s.copyTo(qo),s=qo,Rt.matrixAppendRotationInv(s,e.rotate,a,h),a=0,h=0),a-=n/2,h-=o/2,t.roundPixels?(i.context.setTransform(s.a,s.b,s.c,s.d,s.tx*i.resolution|0,s.ty*i.resolution|0),a|=0,h|=0):i.context.setTransform(s.a,s.b,s.c,s.d,s.tx*i.resolution,s.ty*i.resolution);var c=e.baseTexture.resolution,d=i._outerBlend;d&&(r.save(),r.beginPath(),r.rect(a*i.resolution,h*i.resolution,n*i.resolution,o*i.resolution),r.clip()),16777215!==t.tint?(t._cachedTint===t.tint&&t._tintedCanvas.tintId===t._texture._updateID||(t._cachedTint=t.tint,t._tintedCanvas=zo.getTintedCanvas(t,t.tint)),r.drawImage(t._tintedCanvas,0,0,Math.floor(n*c),Math.floor(o*c),Math.floor(a*i.resolution),Math.floor(h*i.resolution),Math.floor(n*i.resolution),Math.floor(o*i.resolution))):r.drawImage(u,e._frame.x*c,e._frame.y*c,Math.floor(n*c),Math.floor(o*c),Math.floor(a*i.resolution),Math.floor(h*i.resolution),Math.floor(n*i.resolution),Math.floor(o*i.resolution)),d&&r.restore(),i.setBlendMode(b.NORMAL)}},Jo.prototype.destroy=function(){this.renderer=null},Kr.prototype._tintedCanvas=null,Kr.prototype._renderCanvas=function(t){t.plugins.sprite.render(this)}; +/*! + * @pixi/canvas-extract - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/canvas-extract is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +var Zo=new Lt,Ko=function(t){this.renderer=t,t.extract=this};Ko.prototype.image=function(t,e,i){var r=new Image;return r.src=this.base64(t,e,i),r},Ko.prototype.base64=function(t,e,i){return this.canvas(t).toDataURL(e,i)},Ko.prototype.canvas=function(t){var e,i,r,n,o=this.renderer;t&&(n=t instanceof De?t:o.generateTexture(t)),n?(e=n.baseTexture._canvasRenderTarget.context,i=n.baseTexture._canvasRenderTarget.resolution,r=n.frame):(e=o.rootContext,i=o.resolution,(r=Zo).width=this.renderer.width,r.height=this.renderer.height);var s=Math.floor(r.width*i),a=Math.floor(r.height*i),h=new ht(s,a,1),u=e.getImageData(r.x*i,r.y*i,s,a);return h.context.putImageData(u,0,0),h.canvas},Ko.prototype.pixels=function(t){var e,i,r,n,o=this.renderer;return t&&(n=t instanceof De?t:o.generateTexture(t)),n?(e=n.baseTexture._canvasRenderTarget.context,i=n.baseTexture._canvasRenderTarget.resolution,r=n.frame):(e=o.rootContext,(r=Zo).width=o.width,r.height=o.height),e.getImageData(0,0,r.width*i,r.height*i).data},Ko.prototype.destroy=function(){this.renderer.extract=null,this.renderer=null}; +/*! + * @pixi/canvas-prepare - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/canvas-prepare is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +var Qo=16,$o=function(t){function e(e){t.call(this,e),this.uploadHookHelper=this,this.canvas=document.createElement("canvas"),this.canvas.width=Qo,this.canvas.height=Qo,this.ctx=this.canvas.getContext("2d"),this.registerUploadHook(ts)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.destroy=function(){t.prototype.destroy.call(this),this.ctx=null,this.canvas=null},e}(dn);function ts(t,e){if(e instanceof ge){var i=e.source,r=0===i.width?t.canvas.width:Math.min(t.canvas.width,i.width),n=0===i.height?t.canvas.height:Math.min(t.canvas.height,i.height);return t.ctx.drawImage(i,0,0,r,n,0,0,t.canvas.width,t.canvas.height),!0}return!1} +/*! + * @pixi/canvas-sprite-tiling - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/canvas-sprite-tiling is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */uo.prototype._renderCanvas=function(t){var e=this._texture;if(e.baseTexture.valid){var i=t.context,r=this.worldTransform,n=t.resolution,o=e.baseTexture,s=o.getDrawableSource(),a=o.resolution,h=this.tilePosition.x/this.tileScale.x%e._frame.width*a,u=this.tilePosition.y/this.tileScale.y%e._frame.height*a;if(this._textureID!==this._texture._updateID||this._cachedTint!==this.tint){this._textureID=this._texture._updateID;var l=new ht(e._frame.width,e._frame.height,a);16777215!==this.tint?(this._tintedCanvas=zo.getTintedCanvas(this,this.tint),l.context.drawImage(this._tintedCanvas,0,0)):l.context.drawImage(s,-e._frame.x*a,-e._frame.y*a),this._cachedTint=this.tint,this._canvasPattern=l.context.createPattern(l.canvas,"repeat")}i.globalAlpha=this.worldAlpha,i.setTransform(r.a*n,r.b*n,r.c*n,r.d*n,r.tx*n,r.ty*n),t.setBlendMode(this.blendMode),i.fillStyle=this._canvasPattern,i.scale(this.tileScale.x/a,this.tileScale.y/a);var c=this.anchor.x*-this._width,d=this.anchor.y*-this._height;this.uvRespectAnchor?(i.translate(h,u),i.fillRect(-h+c,-u+d,this._width/this.tileScale.x*a,this._height/this.tileScale.y*a)):(i.translate(h+c,u+d),i.fillRect(-h,-u,this._width/this.tileScale.x*a,this._height/this.tileScale.y*a))}}, +/*! + * @pixi/canvas-particles - v5.1.2 + * Compiled Sat, 24 Aug 2019 01:06:18 UTC + * + * @pixi/canvas-particles is licensed under the MIT License. + * http://www.opensource.org/licenses/mit-license + */ +to.prototype.renderCanvas=function(t){if(this.visible&&!(this.worldAlpha<=0)&&this.children.length&&this.renderable){var e=t.context,i=this.worldTransform,r=!0,n=0,o=0,s=0,a=0;t.setBlendMode(this.blendMode),e.globalAlpha=this.worldAlpha,this.displayObjectUpdateTransform();for(var h=0;h=n-s?p():u<=s?f():this._doOnCenterTap&&this._doOnCenterTap();break;case"rtl":u<=s?p():u>=n-s?f():this._doOnCenterTap&&this._doOnCenterTap();break;case"ttb":l>=o-a?p():l<=a?f():this._doOnCenterTap&&this._doOnCenterTap();break;case"btt":l<=a?p():l>=o-a?f():this._doOnCenterTap&&this._doOnCenterTap()}}else this._doOnCenterTap&&this._doOnCenterTap()}},{key:"_handleSwipe",value:function(t){var e=this._storyData,i=e.readingProgression,r=e.goForward,n=e.goBackward;switch(i){case"ltr":"swipeleft"===t.type?r():"swiperight"===t.type&&n();break;case"rtl":"swiperight"===t.type?r():"swipeleft"===t.type&&n();break;case"ttb":"swipeup"===t.type?r():"swipedown"===t.type&&n();break;case"btt":"swipedown"===t.type?r():"swipeup"===t.type&&n()}}},{key:"_resetScroll",value:function(){this._lastScrollEvent={deltaX:0,deltaY:0}}},{key:"_onMobileScroll",value:function(t){if("panend"===t.type){var e=this._storyData,i=e.snapBehavior,r=e.goToClosestSnapPoint;switch(i){case"sticky":r();break;default:this._releaseScroll(t)}this._resetScroll()}else if(this._lastScrollEvent){var n={deltaX:this._lastScrollEvent.deltaX-t.deltaX,deltaY:this._lastScrollEvent.deltaY-t.deltaY};this._scroll(n),this._lastScrollEvent=t}}},{key:"_releaseScroll",value:function(t){var e={x:10*t.velocityX,y:10*t.velocityY},i=Date.now();this._autoScroll(e,i)}},{key:"_autoScroll",value:function(t,e){var i=Date.now()-e,r=-t.x*Math.exp(-i/325),n=-t.y*Math.exp(-i/325);Math.abs(r)<1||Math.abs(n)<1?(r=Math.round(2*r)/2,n=Math.round(2*n)/2):(r=Math.round(r),n=Math.round(n)),(Math.abs(r)>.5||Math.abs(n)>.5)&&(this._scroll({deltaX:r,deltaY:n}),requestAnimationFrame(this._autoScroll.bind(this,t,e)))}},{key:"_scroll",value:function(t){(0,this._storyData.setScrollDelta)({deltaX:t.deltaX,deltaY:t.deltaY})}},{key:"_onWheelScroll",value:function(t){t.preventDefault(),this._scroll(t)}},{key:"setOnCenterTapAction",value:function(t){this._doOnCenterTap=t}},{key:"destroy",value:function(){this._mc.off("tap",this._handleTap.bind(this,this._player)),this._mc.off("swipeleft swiperight swipeup swipedown",this._handleSwipe.bind(this)),this._mc.off("panleft panright panup pandown panend",this._onMobileScroll.bind(this))}}])&&us(e.prototype,i),r&&us(e,r),t}();function cs(t,e){for(var i=0;i1?i[1]:null}}function bs(t,e,i){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;if(!e)return{};var n=xs(t,e),o=n.path,s=n.mediaFragmentString,a=null;!0===function(t,e){return _s(t,e,"image",["jpg","jpeg","png","JPG","JPEG","PNG","webp"])}(e,i)?a="image":!0===function(t,e){return _s(t,e,"video",["mp4","mov","m4v","webm"])}(e,i)&&(a="video");var h={href:e,path:o,mediaFragmentString:s,resourceType:a};if(r){var u=xs(t,r);h=function(t){for(var e=1;e=-.5?(this._isLargerThanViewport=!1,this._pageContainer.position.x=-e/2+r/2):(this._isLargerThanViewport=!0,this._startPosition.x=r/2,this._endPosition.x=r/2-e);break;case"ttb":(e-=n)<=.5?(this._isLargerThanViewport=!1,this._pageContainer.position.x=-e/2-n/2):(this._startPosition.y=-n/2,this._isLargerThanViewport=!0,this._endPosition.y=-e-n/2);break;case"btt":(e+=n)>=-.5?(this._isLargerThanViewport=!1,this._pageContainer.position.x=-e/2+n/2):(this._isLargerThanViewport=!0,this._startPosition.y=-n/2,this._endPosition.y=n/2-e)}this._totalDistanceToCover=e,!0===this._isLargerThanViewport?this._buildRelevantSnapPoints():this._snapPointsArray=null}},{key:"_buildRelevantSnapPoints",value:function(){var t=this,e=[],i=-1;this._rawSnapPointsArray.forEach(function(r){var n=t._getProgressForSnapPoint(r);if(null!==n&&n>i){var o=function(t){for(var e=1;e0){var o=Math.min(Math.max(this._pageContainer.position.x-e,this._startPosition.x),this._startPosition.x+n);r=Math.abs((o-this._startPosition.x)/n)}else if(n<0){var s=Math.min(Math.max(this._pageContainer.position.x-e,this._startPosition.x+n),this._startPosition.x);r=Math.abs((s-this._startPosition.x)/n)}var a=this._endPosition.y-this._startPosition.y;if(a>0){var h=Math.min(Math.max(this._pageContainer.position.y-i,this._startPosition.y),this._startPosition.y+a);r=Math.abs((h-this._startPosition.y)/a)}else if(a<0){var u=Math.min(Math.max(this._pageContainer.position.y-i,this._startPosition.y+a),this._startPosition.y);r=Math.abs((u-this._startPosition.y)/a)}this._setProgress(r)}}},{key:"goToNextSnapPoint",value:function(){var t=this._getNextSnapPointProgress();null!==t&&this._startSnapPointJump(t)}},{key:"goToPreviousSnapPoint",value:function(){var t=this._getPreviousSnapPointProgress();null!==t&&this._startSnapPointJump(t)}},{key:"_getNextSnapPointProgress",value:function(){if(!this._snapPointsArray)return null;for(var t=0;t=0&&this._snapPointsArray[t].progress>=this._progress;)t-=1;var e=0;return t>=0&&(e=this._snapPointsArray[t].progress),e}},{key:"_startSnapPointJump",value:function(t,e){this._snapPointJumpData={isAutoScrolling:!0,isGoingForward:t-this._progress>=0,targetProgress:t,startProgress:this._progress,startDate:Date.now(),mode:e},requestAnimationFrame(this._autoProgress.bind(this))}},{key:"goToClosestSnapPoint",value:function(){if(!1!==this._isLargerThanViewport){var t=this._getNextSnapPointProgress(),e=this._getPreviousSnapPointProgress(),i=t-this._progress,r=this._progress-e,n=this._progress;ir&&(n=e),this._startSnapPointJump(n,"sticky")}}},{key:"_autoProgress",value:function(){if(!1!==this._isAutoScrolling){var t=this._snapPointJumpData,e=t.shouldForceToBacktrack,i=t.shouldForceToEnd,r=t.targetProgress,n=t.startProgress,o=t.startDate,s=t.mode,a=t.endCallback,h=Math.abs((r-n)*this._totalDistanceToCover);this._changeDuration="sticky"===s?h/this._stickyMoveSpeed:h/this._snapJumpSpeed;var u=(Date.now()-o)/this._changeDuration;if(!0===e)this._reset(),a&&a();else if(u>=1||!0===i)this._setProgress(r),this._reset();else{var l=n+(r-n)*u;this._setProgress(l),requestAnimationFrame(this._autoProgress.bind(this))}}}},{key:"forceJump",value:function(t,e){this._snapPointJumpData&&(e!==this._snapPointJumpData.isGoingForward?(this._snapPointJumpData.endCallback=t,this._snapPointJumpData.shouldForceToBacktrack=!0):this._snapPointJumpData.shouldForceToEnd=!0)}},{key:"getSliceIndexInPageForPath",value:function(t){if(!this._slicesArray||0===this._slicesArray.length)return null;for(var e=!1,i=0;i1&&void 0!==arguments[1]?arguments[1]:null;if(!t)return null;var i=Kn.shared.resources;if(!i||!i[t])return null;var r=i[t].texture;if(this._naturalWidth=r.width,this._naturalHeight=r.height,e){var n=function(t){var e=t.split("=");if(2!==e.length)return null;var i=e[1];if(2!==(i=i.split(":")).length)return null;var r=fs(i,2),n=r[0],o=r[1];if("percent"!==n&&"pixel"!==n)return null;var s=o.split(",");if(4!==s.length)return null;var a=fs(s,4),h=a[0],u=a[1],l=a[2],c=a[3];return h=Number(h),u=Number(u),l=Number(l),c=Number(c),gs(h)&&gs(u)&&gs(l)&&gs(c)?{unit:n,x:h,y:u,w:l,h:c}:null}(e);if(!n)return r;var o=n.unit,s=n.x,a=n.y,h=n.w,u=n.h,l=r,c=l.width,d=l.height;"percent"===o&&(s*=c/100,a*=d/100,h*=c/100,u*=d/100),s=Math.min(s,c),a=Math.min(a,d),h=Math.min(h,c),u=Math.min(u,d);var p=r.baseTexture,f=new Lt(s,a,h,u);r=new Ie(p,f),this._naturalWidth=r.width,this._naturalHeight=r.height}return r}},{key:"_doOnDurationChange",value:function(){var t=this._video,e=t.duration,i=t.videoWidth,r=t.videoHeight;if(e){var n=Ie.from(this._video);n.baseTexture.resource.autoPlay=!1,this._playableSprite=this._createAndAddSprite(),this._playableSprite.texture=n,this._playableSprite.anchor.set(.5),this._naturalWidth=i,this._naturalHeight=r,this._duration=e,this.resize(),!0===this._shouldPlay&&this._video.play()}}},{key:"addSliceContainerToPageContainer",value:function(t){t.addChild(this._container)}},{key:"play",value:function(){this._video&&this._video.duration&&(this._naturalWidth&&this._naturalHeight?this._video.play():this._shouldPlay=!0)}},{key:"stop",value:function(){this._video&&this._video.duration&&(this._video.pause(),this._video.currentTime=0)}},{key:"resize",value:function(){this._applyFit(),this._applyOverflow()}},{key:"_applyFit",value:function(){if(this._naturalWidth&&this._naturalHeight){var t=this._naturalWidth/this._naturalHeight,e=this._player.viewportSize,i=e.width,r=e.height,n=i/r,o=1;switch(this._fit){case"height":o=this._getScaleWhenForcingHeight(r);break;case"width":o=this._getScaleWhenForcingWidth(i);break;case"contain":o=t>=n?this._getScaleWhenForcingWidth(i):this._getScaleWhenForcingHeight(r);break;case"cover":o=t>=n?this._getScaleWhenForcingHeight(r):this._getScaleWhenForcingWidth(i)}this._container.scale.set(o),this._scale=o}}},{key:"_getScaleWhenForcingHeight",value:function(t){return t/this._naturalHeight}},{key:"_getScaleWhenForcingWidth",value:function(t){return t/this._naturalWidth}},{key:"_applyOverflow",value:function(){if("clipped"===this._overflow){var t=this._scale||1,e=this._player.viewportSize,i=e.width,r=e.height;this._mask.clear(),this._mask.beginFill(255),this._mask.drawRect(-i/t/2,-r/t/2,i/t,r/t),this._mask.endFill()}}},{key:"destroy",value:function(){this._video&&this._video.removeEventListener("durationchange",this._doOnDurationChange.bind(this)),this._playableSprite&&this._destroySprite(this._playableSprite),this._sprite&&this._destroySprite(this._sprite)}},{key:"_destroySprite",value:function(t){var e=t.texture;e&&this._destroyTexture(e),t.destroy({children:!0,texture:!1,baseTexture:!1})}},{key:"_destroyTexture",value:function(t){if(t){var e=t.baseTexture;e&&e.destroy();t.destroy(!1)}}}]),t}();function Os(t){return(Os="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Is(t,e){for(var i=0;i0?i:0,s._resourceType="sequence",s}var i,r,n;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Rs(t,e)}(e,Cs),i=e,(r=[{key:"finishBuilding",value:function(){var t=this._createTexturesArray();0!==t.length&&(this._playableSprite=this._createAndAddAnimatedSprite(t),this._playableSprite.loop=!1,this._centerAndClipIfNeeded())}},{key:"_createAndAddAnimatedSprite",value:function(t){var e=new No(t);return this._container.addChild(e),e}},{key:"_createTexturesArray",value:function(){var t=this,e=[];if(this._resourceDataArray&&this._resourceDataArray.length>0){var i=[];if(this._resourceDataArray.forEach(function(e){var r=e.path,n=e.mediaFragmentString,o=t._getTextureFromPath(r,n);o&&(!o||t._naturalWidth&&t._naturalHeight||(t._naturalWidth=o.width,t._naturalHeight=o.height),i.push(o))}),i.length>0){var r=this._duration/this._resourceDataArray.length;e=i.map(function(t){return{texture:t,time:r}})}}return e}},{key:"play",value:function(){this._playableSprite&&this._playableSprite.play()}},{key:"gotoAndStop",value:function(t){this._playableSprite&&this._playableSprite.gotoAndStop(t)}},{key:"destroy",value:function(){var t=this;this._playableSprite&&this._playableSprite.textures&&this._playableSprite.textures.forEach(function(e){t._destroyTexture(e)})}}])&&Is(i.prototype,r),n&&Is(i,n),e}();function Fs(t){return function(t){if(Array.isArray(t)){for(var e=0,i=new Array(t.length);e=n||!0===e)this._endTransition();else{var a=s/n,h=this._player.viewportSize,u=h.width,l=h.height;if("dissolve"===i&&(this._newPageContainer.alpha=a),"slide-in"===i||"push"===i)switch(r){case"ltr":this._newPageContainer.position.x=(a-1)*u;break;case"rtl":this._newPageContainer.position.x=(1-a)*u;break;case"ttb":this._newPageContainer.position.y=(a-1)*l;break;case"btt":this._newPageContainer.position.y=(1-a)*l}if("slide-out"===i||"push"===i)switch(r){case"ltr":this._oldPageContainer.position.x=a*u;break;case"rtl":this._oldPageContainer.position.x=-a*u;break;case"ttb":this._oldPageContainer.position.y=a*l;break;case"btt":this._oldPageContainer.position.y=-a*l}this._newPageContainer.visible=!0,requestAnimationFrame(this._transitionLoop.bind(this))}}},{key:"_endTransition",value:function(){var t,e=this._transitionData,i=e.sequenceSlice,r=e.videoSlice,n=e.newPage,o=e.oldPage,s=e.endCallback;this._oldPageContainer.position={x:0,y:0},this._newPageContainer.position={x:0,y:0},this._newPageContainer.alpha=1,this._newPageContainer.visible=!0,(t=this._contentContainer).addChild.apply(t,Fs(this._newPageContainer.children)),i&&i.gotoAndStop(0),r&&r.stop(),this._contentContainer.removeChild(this._newPageContainer),this._oldPageContainer.removeChildren(),this._contentContainer.removeChild(this._oldPageContainer),this._reset(),s&&s(),n&&n.play(),o&&o.stop()}},{key:"forceTransitionToEnd",value:function(t,e){this._transitionData&&(e!==this._transitionData.isGoingForward&&(this._transitionData.endCallback=t),this._transitionData.shouldForceToEnd=!0)}},{key:"resize",value:function(){if(this._transitionData&&!0===this._transitionData.isRunning){var t=this._transitionData,e=t.sequenceSlice,i=t.videoSlice,r=t.newPage,n=t.oldPage;e&&e.resize(),i&&i.resize(),n&&n.resize(),r&&r.resize()}}},{key:"destroy",value:function(){this._sequenceSlicesArray.forEach(function(t){t.destroy()}),this._videoSlicesArray.forEach(function(t){t.destroy()})}}]),t}();function js(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function Hs(t,e){for(var i=0;ithis._pagesArray.length-1)){var i=null===this._pageIndex||t-this._pageIndex>=0;this._pageIndex=t;var r=this._currentPage;this._currentPage=this._pagesArray[this._pageIndex],this._currentPage.resize();var n=null;r&&!0===i&&(n=r.transitionForward),!1===i&&(n=this._currentPage.transitionBackward),n&&void 0===e?this._transitionManager&&this._transitionManager.run(n,this._currentPage,r,i):(this._contentContainer.removeChildren(),this._currentPage.pushContentIntoContainer(this._contentContainer),this._currentPage.play(),r&&r.stop(),void 0!==e&&this._currentPage.goToSliceIndex(e,i))}}},{key:"_goForward",value:function(){this._turnPageOrJumpWithinAfterForcingIfRelevant(!0)}},{key:"_goBackward",value:function(){this._turnPageOrJumpWithinAfterForcingIfRelevant(!1)}},{key:"_turnPageOrJumpWithinAfterForcingIfRelevant",value:function(t){if(this._currentPage){var e=!0===t?this._turnPageOrJumpWithinForward.bind(this):this._turnPageOrJumpWithinBackward.bind(this);!0===this._currentPage.isAutoScrolling?this._currentPage.forceJump(e,t):this._transitionManager&&!0===this._transitionManager.isRunning?this._transitionManager.forceTransitionToEnd(e,t):e()}}},{key:"_turnPageOrJumpWithinForward",value:function(){this._currentPage&&(!0===this._currentPage.isAtEnd?this._goToNextPage():this._goToNextSnapPoint())}},{key:"_goToNextPage",value:function(){this._goToPageWithIndex(this._pageIndex+1)}},{key:"_goToNextSnapPoint",value:function(){this._currentPage.goToNextSnapPoint()}},{key:"_turnPageOrJumpWithinBackward",value:function(){this._currentPage&&(!0===this._currentPage.isAtStart?this._goToPreviousPage():this._goToPreviousSnapPoint())}},{key:"_goToPreviousPage",value:function(){this._goToPageWithIndex(this._pageIndex-1)}},{key:"_goToPreviousSnapPoint",value:function(){this._currentPage&&this._currentPage.goToPreviousSnapPoint()}},{key:"_goToClosestSnapPoint",value:function(){this._currentPage&&this._currentPage.goToClosestSnapPoint()}},{key:"_setScrollDelta",value:function(t){!this._currentPage||!0===this._currentPage.isAutoScrolling||this._transitionManager&&!0===this._transitionManager.isRunning||this._currentPage.applyScrollDelta(t)}},{key:"goTo",value:function(t){if(this._pagesArray&&0!==this._pagesArray.length){for(var e=!1,i=0,r=0;i=o){if(r=i*o,!this._app.renderer)return;this._app.renderer.view.style.marginLeft="".concat((e-r)/2,"px"),this._app.renderer.view.style.marginTop="0px"}else if(sn&&(r=null,n=null),this._minRatio=r,this._maxRatio=n,this._resize()}}},{key:"openDiViNaFromPath",value:function(t,e){this._interactionManager.setOnCenterTapAction(e),this._createResourceManager(),this._createStory(),this._story.loadFromPath(t)}},{key:"_createResourceManager",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this._resourceManager=new ds(this._textManager,t)}},{key:"_createStory",value:function(){this._story=new zs(this,this._textManager,this._interactionManager,this._resourceManager,this._contentContainer,this._setRatioConstraint.bind(this))}},{key:"openDiViNaFromData",value:function(t){this._createResourceManager(t.base64DataByHref),this._createStory(),this._story.loadFromData(t)}},{key:"_removeTextManager",value:function(){this._textManager.destroy(),this._textManager=null}},{key:"goTo",value:function(t){this._story&&this._story.goTo(t)}},{key:"destroy",value:function(){window.removeEventListener("resize",this._resize.bind(this)),this._story.destroy(),this._resourceManager.destroy(),this._interactionManager.destroy(),this._textManager&&this._textManager.destroy();this._app.destroy(!0,{children:!0,texture:!1,baseTexture:!1})}}]),t}();e.default=Ys}]).default; +//# sourceMappingURL=divinaPlayer.js.map \ No newline at end of file diff --git a/r2-navigator/src/main/assets/divina/divinaTouchHandling.js b/r2-navigator/src/main/assets/divina/divinaTouchHandling.js new file mode 100644 index 00000000..53797cae --- /dev/null +++ b/r2-navigator/src/main/assets/divina/divinaTouchHandling.js @@ -0,0 +1,65 @@ +var singleTouchGesture = false; +var startX = 0; +var startY = 0; +var availWidth = window.screen.availWidth; +var availHeight = window.screen.availHeight; + + +window.addEventListener("load", function(){ // on page load + // Get screen X and Y sizes. + // Events listeners for the touches. + window.document.addEventListener("touchstart", handleTouchStart, false); + window.document.addEventListener("touchend", handleTouchEnd, false); + // When device orientation changes, screen X and Y sizes are recalculated. + }, false); + + + +// When a touch is detected records its starting coordinates and if it's a singleTouchGesture. +var handleTouchStart = function(event) { + if (event.target.nodeName.toUpperCase() === 'A') { + console.log("Touched a link."); + // singleTouchGesture = false; + return; + } + console.log("Touch sent to native code."); + singleTouchGesture = event.touches.length == 1; + + var touch = event.changedTouches[0]; + + startX = touch.screenX % availWidth; + startY = touch.screenY % availHeight; + +}; + +// When a touch ends, check if any action has to be made, and contact native code. +var handleTouchEnd = function(event) { + if(!singleTouchGesture) { + return; + } + + var touch = event.changedTouches[0]; + + var relativeDistanceX = Math.abs(((touch.screenX % availWidth) - startX) / availWidth); + var relativeDistanceY = Math.abs(((touch.screenY % availHeight) - startY) / availHeight); + var touchDistance = Math.max(relativeDistanceX, relativeDistanceY); + + var scrollWidth = document.scrollWidth; + var screenWidth = availWidth; + var tapAreaWidth = availWidth * 0.2; + + if(touchDistance < 0.01) { + var position = (touch.screenX % availWidth) / availWidth; + if (position <= 0.2) { + console.log("LeftTapped"); + } else if(position >= 0.8) { + console.log("RightTapped"); + } else { + console.log("CenterTapped"); + Android.centerTapped(); + } + event.stopPropagation(); + event.preventDefault(); + return; + } +}; diff --git a/r2-navigator/src/main/assets/scripts/crypto-sha256.js b/r2-navigator/src/main/assets/scripts/crypto-sha256.js new file mode 100644 index 00000000..f0bb0bc2 --- /dev/null +++ b/r2-navigator/src/main/assets/scripts/crypto-sha256.js @@ -0,0 +1,974 @@ +/* + CryptoJS v3.1.2 + code.google.com/p/crypto-js + (c) 2009-2013 by Jeff Mott. All rights reserved. + code.google.com/p/crypto-js/wiki/License + */ + + +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(); + } + else if (typeof define === "function" && define.amd) { + // AMD + define([], factory); + } + else { + // Global (browser) + root.CryptoJS = factory(); + } +}(this, function () { + + /** + * CryptoJS core components. + */ + var CryptoJS = CryptoJS || (function (Math, undefined) { + /* + * Local polyfil of Object.create + */ + var create = Object.create || (function () { + function F() {}; + + return function (obj) { + var subtype; + + F.prototype = obj; + + subtype = new F(); + + F.prototype = null; + + return subtype; + }; + }()) + + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + var subtype = create(this); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init') || this.init === subtype.init) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + + var r = (function (m_w) { + var m_w = m_w; + var m_z = 0x3ade68b1; + var mask = 0xffffffff; + + return function () { + m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; + m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; + var result = ((m_z << 0x10) + m_w) & mask; + result /= 0x100000000; + result += 0.5; + return result * (Math.random() > .5 ? 1 : -1); + } + }); + + for (var i = 0, rcache; i < nBytes; i += 4) { + var _r = r((rcache || Math.random()) * 0x100000000); + + rcache = _r() * 0x3ade67b7; + words.push((_r() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; + }(Math)); + + + return CryptoJS; + +})); + +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +;(function (root, factory) { + if (typeof exports === "object") { + // CommonJS + module.exports = exports = factory(require("./core")); + } + else if (typeof define === "function" && define.amd) { + // AMD + define(["./core"], factory); + } + else { + // Global (browser) + factory(root.CryptoJS); + } +}(this, function (CryptoJS) { + + (function (Math) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var Hasher = C_lib.Hasher; + var C_algo = C.algo; + + // Initialization and round constants tables + var H = []; + var K = []; + + // Compute constants + (function () { + function isPrime(n) { + var sqrtN = Math.sqrt(n); + for (var factor = 2; factor <= sqrtN; factor++) { + if (!(n % factor)) { + return false; + } + } + + return true; + } + + function getFractionalBits(n) { + return ((n - (n | 0)) * 0x100000000) | 0; + } + + var n = 2; + var nPrime = 0; + while (nPrime < 64) { + if (isPrime(n)) { + if (nPrime < 8) { + H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2)); + } + K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3)); + + nPrime++; + } + + n++; + } + }()); + + // Reusable object + var W = []; + + /** + * SHA-256 hash algorithm. + */ + var SHA256 = C_algo.SHA256 = Hasher.extend({ + _doReset: function () { + this._hash = new WordArray.init(H.slice(0)); + }, + + _doProcessBlock: function (M, offset) { + // Shortcut + var H = this._hash.words; + + // Working variables + var a = H[0]; + var b = H[1]; + var c = H[2]; + var d = H[3]; + var e = H[4]; + var f = H[5]; + var g = H[6]; + var h = H[7]; + + // Computation + for (var i = 0; i < 64; i++) { + if (i < 16) { + W[i] = M[offset + i] | 0; + } else { + var gamma0x = W[i - 15]; + var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ + ((gamma0x << 14) | (gamma0x >>> 18)) ^ + (gamma0x >>> 3); + + var gamma1x = W[i - 2]; + var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ + ((gamma1x << 13) | (gamma1x >>> 19)) ^ + (gamma1x >>> 10); + + W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; + } + + var ch = (e & f) ^ (~e & g); + var maj = (a & b) ^ (a & c) ^ (b & c); + + var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); + var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); + + var t1 = h + sigma1 + ch + K[i] + W[i]; + var t2 = sigma0 + maj; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Intermediate hash value + H[0] = (H[0] + a) | 0; + H[1] = (H[1] + b) | 0; + H[2] = (H[2] + c) | 0; + H[3] = (H[3] + d) | 0; + H[4] = (H[4] + e) | 0; + H[5] = (H[5] + f) | 0; + H[6] = (H[6] + g) | 0; + H[7] = (H[7] + h) | 0; + }, + + _doFinalize: function () { + // Shortcuts + var data = this._data; + var dataWords = data.words; + + var nBitsTotal = this._nDataBytes * 8; + var nBitsLeft = data.sigBytes * 8; + + // Add padding + dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); + dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; + data.sigBytes = dataWords.length * 4; + + // Hash final blocks + this._process(); + + // Return final computed hash + return this._hash; + }, + + clone: function () { + var clone = Hasher.clone.call(this); + clone._hash = this._hash.clone(); + + return clone; + } + }); + + /** + * Shortcut function to the hasher's object interface. + * + * @param {WordArray|string} message The message to hash. + * + * @return {WordArray} The hash. + * + * @static + * + * @example + * + * var hash = CryptoJS.SHA256('message'); + * var hash = CryptoJS.SHA256(wordArray); + */ + C.SHA256 = Hasher._createHelper(SHA256); + + /** + * Shortcut function to the HMAC's object interface. + * + * @param {WordArray|string} message The message to hash. + * @param {WordArray|string} key The secret key. + * + * @return {WordArray} The HMAC. + * + * @static + * + * @example + * + * var hmac = CryptoJS.HmacSHA256(message, key); + */ + C.HmacSHA256 = Hasher._createHmacHelper(SHA256); + }(Math)); + + + return CryptoJS.SHA256; + +})); diff --git a/r2-navigator/src/main/assets/scripts/highlight.js b/r2-navigator/src/main/assets/scripts/highlight.js new file mode 100644 index 00000000..af7826b2 --- /dev/null +++ b/r2-navigator/src/main/assets/scripts/highlight.js @@ -0,0 +1,1964 @@ +// +// highlight.js +// r2-navigator-kotlin +// +// Organized by Taehyun Kim on 6/27/19 from r2-navigator-js. +// +// Copyright 2019 Readium Foundation. All rights reserved. +// Use of this source code is governed by a BSD-style license which is detailed +// in the LICENSE file present in the project repository where this source code is maintained. +// + + +const ROOT_CLASS_REDUCE_MOTION = "r2-reduce-motion"; +const ROOT_CLASS_NO_FOOTNOTES = "r2-no-popup-foonotes"; +const POPUP_DIALOG_CLASS = "r2-popup-dialog"; +const FOOTNOTES_CONTAINER_CLASS = "r2-footnote-container"; +const FOOTNOTES_CLOSE_BUTTON_CLASS = "r2-footnote-close"; +const FOOTNOTE_FORCE_SHOW = "r2-footnote-force-show"; +const TTS_ID_PREVIOUS = "r2-tts-previous"; +const TTS_ID_NEXT = "r2-tts-next"; +const TTS_ID_SLIDER = "r2-tts-slider"; +const TTS_ID_ACTIVE_WORD = "r2-tts-active-word"; +const TTS_ID_CONTAINER = "r2-tts-txt"; +const TTS_ID_INFO = "r2-tts-info"; +const TTS_NAV_BUTTON_CLASS = "r2-tts-button"; +const TTS_ID_SPEAKING_DOC_ELEMENT = "r2-tts-speaking-el"; +const TTS_CLASS_INJECTED_SPAN = "r2-tts-speaking-txt"; +const TTS_CLASS_INJECTED_SUBSPAN = "r2-tts-speaking-word"; +const TTS_ID_INJECTED_PARENT = "r2-tts-speaking-txt-parent"; +const ID_HIGHLIGHTS_CONTAINER = "R2_ID_HIGHLIGHTS_CONTAINER"; +const ID_ANNOTATION_CONTAINER = "R2_ID_ANNOTATION_CONTAINER"; +const CLASS_HIGHLIGHT_CONTAINER = "R2_CLASS_HIGHLIGHT_CONTAINER"; +const CLASS_ANNOTATION_CONTAINER = "R2_CLASS_ANNOTATION_CONTAINER"; +const CLASS_HIGHLIGHT_AREA = "R2_CLASS_HIGHLIGHT_AREA"; +const CLASS_ANNOTATION_AREA = "R2_CLASS_ANNOTATION_AREA"; +const CLASS_HIGHLIGHT_BOUNDING_AREA = "R2_CLASS_HIGHLIGHT_BOUNDING_AREA"; +const CLASS_ANNOTATION_BOUNDING_AREA = "R2_CLASS_ANNOTATION_BOUNDING_AREA"; +// tslint:disable-next-line:max-line-length +const _blacklistIdClassForCFI = [POPUP_DIALOG_CLASS, TTS_CLASS_INJECTED_SPAN, TTS_CLASS_INJECTED_SUBSPAN, ID_HIGHLIGHTS_CONTAINER, CLASS_HIGHLIGHT_CONTAINER, CLASS_HIGHLIGHT_AREA, CLASS_HIGHLIGHT_BOUNDING_AREA, "resize-sensor"]; +const CLASS_PAGINATED = "r2-css-paginated"; + +//const IS_DEV = (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev"); +const IS_DEV = false; +const _highlights = []; + +let _highlightsContainer; +let _annotationContainer; +let lastMouseDownX = -1; +let lastMouseDownY = -1; +let bodyEventListenersSet = false; + +const USE_SVG = false; +const DEFAULT_BACKGROUND_COLOR_OPACITY = 0.3; +const ALT_BACKGROUND_COLOR_OPACITY = 0.45; + +//const DEBUG_VISUALS = false; +const DEBUG_VISUALS = false; +const DEFAULT_BACKGROUND_COLOR = { + blue: 100, + green: 50, + red: 230, +}; + +const ANNOTATION_WIDTH = 15; + +function resetHighlightBoundingStyle(_win, highlightBounding) { + + if (highlightBounding.getAttribute("class") == CLASS_ANNOTATION_BOUNDING_AREA ) { + return; + } + highlightBounding.style.outline = "none"; + highlightBounding.style.setProperty("background-color", "transparent", "important"); +} + +function setHighlightAreaStyle(win, highlightAreas, highlight) { + const useSVG = !DEBUG_VISUALS && USE_SVG; + for (const highlightArea of highlightAreas) { + const isSVG = useSVG && highlightArea.namespaceURI === SVG_XML_NAMESPACE; + const opacity = ALT_BACKGROUND_COLOR_OPACITY; + if (isSVG) { + highlightArea.style.setProperty("fill", `rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue})`, "important"); + highlightArea.style.setProperty("fill-opacity", `${opacity}`, "important"); + highlightArea.style.setProperty("stroke", `rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue})`, "important"); + highlightArea.style.setProperty("stroke-opacity", `${opacity}`, "important"); + } + else { + highlightArea.style.setProperty("background-color", `rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity})`, "important"); + } + } +} + +function resetHighlightAreaStyle(win, highlightArea) { + const useSVG = !DEBUG_VISUALS && USE_SVG; + //const useSVG = USE_SVG; + const isSVG = useSVG && highlightArea.namespaceURI === SVG_XML_NAMESPACE; + const id = isSVG ? + ((highlightArea.parentNode && highlightArea.parentNode.parentNode && highlightArea.parentNode.parentNode.nodeType === Node.ELEMENT_NODE && highlightArea.parentNode.parentNode.getAttribute) ? highlightArea.parentNode.parentNode.getAttribute("id") : undefined) : + ((highlightArea.parentNode && highlightArea.parentNode.nodeType === Node.ELEMENT_NODE && highlightArea.parentNode.getAttribute) ? highlightArea.parentNode.getAttribute("id") : undefined); + if (id) { + const highlight = _highlights.find((h) => { + return h.id === id; + }); + if (highlight) { + const opacity = DEFAULT_BACKGROUND_COLOR_OPACITY; + if (isSVG) { + highlightArea.style.setProperty("fill", `rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue})`, "important"); + highlightArea.style.setProperty("fill-opacity", `${opacity}`, "important"); + highlightArea.style.setProperty("stroke", `rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue})`, "important"); + highlightArea.style.setProperty("stroke-opacity", `${opacity}`, "important"); + } + else { + highlightArea.style.setProperty("background-color", `rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity})`, "important"); + } + } + } +} +function processTouchEvent(win, ev) { + + const document = win.document; + const scrollElement = getScrollingElement(document); + const x = ev.changedTouches[0].clientX; + const y = ev.changedTouches[0].clientY; + if (!_highlightsContainer) { + return; + } + const paginated = isPaginated(document); + const bodyRect = document.body.getBoundingClientRect(); + let xOffset; + let yOffset; + if (navigator.userAgent.match(/Android/i)) { + xOffset = paginated ? (-scrollElement.scrollLeft) : bodyRect.left; + yOffset = paginated ? (-scrollElement.scrollTop) : bodyRect.top; + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + xOffset = paginated ? 0 : (-scrollElement.scrollLeft); + yOffset = paginated ? 0 : (bodyRect.top); + } + let foundHighlight; + let foundElement; + let foundRect; + // _highlights.sort(function(a, b) { + // console.log(JSON.stringify(a.selectionInfo)) + // return a.selectionInfo.cleanText.length < b.selectionInfo.cleanText.length + // }) + for (let i = _highlights.length - 1; i >= 0; i--) { + const highlight = _highlights[i]; + let highlightParent = document.getElementById(`${highlight.id}`); + if (!highlightParent) { + highlightParent = _highlightsContainer.querySelector(`#${highlight.id}`); + } + if (!highlightParent) { + continue; + } + let hit = false; + const highlightFragments = highlightParent.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`); + for (const highlightFragment of highlightFragments) { + const withRect = highlightFragment; + const left = withRect.rect.left + xOffset; + const top = withRect.rect.top + yOffset; + foundRect = withRect.rect + if (x >= left && + x < (left + withRect.rect.width) && + y >= top && + y < (top + withRect.rect.height)) { + + + hit = true; + break; + } + } + if (hit) { + foundHighlight = highlight; + foundElement = highlightParent; + break; + } + } + if (!foundHighlight || !foundElement) { + const highlightBoundings = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + for (const highlightBounding of highlightBoundings) { + resetHighlightBoundingStyle(win, highlightBounding); + } + const allHighlightAreas = Array.from(_highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`)); + for (const highlightArea of allHighlightAreas) { + resetHighlightAreaStyle(win, highlightArea); + } + return; + } + + if (foundElement.getAttribute("data-click")) { + if (ev.type === "mousemove") { + const foundElementHighlightAreas = Array.from(foundElement.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`)); + const allHighlightAreas = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`); + for (const highlightArea of allHighlightAreas) { + if (foundElementHighlightAreas.indexOf(highlightArea) < 0) { + resetHighlightAreaStyle(win, highlightArea); + } + } + setHighlightAreaStyle(win, foundElementHighlightAreas, foundHighlight); + const foundElementHighlightBounding = foundElement.querySelector(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + const allHighlightBoundings = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + for (const highlightBounding of allHighlightBoundings) { + if (!foundElementHighlightBounding || highlightBounding !== foundElementHighlightBounding) { + resetHighlightBoundingStyle(win, highlightBounding); + } + } + if (foundElementHighlightBounding) { + if (DEBUG_VISUALS) { + setHighlightBoundingStyle(win, foundElementHighlightBounding, foundHighlight); + } + } + } + else if (ev.type === "touchstart" || ev.type === "touchend") { + + const size = { + screenWidth: window.outerWidth, + screenHeight: window.outerHeight, + left: foundRect.left, + width: foundRect.width, + top: foundRect.top, + height: foundRect.height + }; + const payload = { + highlight: foundHighlight.id, + size: size + }; + + if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') { + electron_1.ipcRenderer.sendToHost(R2_EVENT_HIGHLIGHT_CLICK, payload); + } else if (window.webkitURL) { + console.log(foundHighlight.id.includes("R2_ANNOTATION_")) + if ( foundHighlight.id.search("R2_ANNOTATION_") >= 0 ) { + if (navigator.userAgent.match(/Android/i)) { + Android.highlightAnnotationMarkActivated(foundHighlight.id); + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + webkit.messageHandlers.highlightAnnotationMarkActivated.postMessage(foundHighlight.id); + } + } else if ( foundHighlight.id.search("R2_HIGHLIGHT_") >= 0 ) { + if (navigator.userAgent.match(/Android/i)) { + Android.highlightActivated(foundHighlight.id); + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + webkit.messageHandlers.highlightActivated.postMessage(foundHighlight.id); + } + } + } + + ev.stopPropagation(); + } + } +} + +function processMouseEvent(win, ev) { + const document = win.document; + const scrollElement = getScrollingElement(document); + const x = ev.clientX; + const y = ev.clientY; + if (!_highlightsContainer) { + return; + } + + const paginated = isPaginated(document); + const bodyRect = document.body.getBoundingClientRect(); + let xOffset; + let yOffset; + if (navigator.userAgent.match(/Android/i)) { + xOffset = paginated ? (-scrollElement.scrollLeft) : bodyRect.left; + yOffset = paginated ? (-scrollElement.scrollTop) : bodyRect.top; + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + xOffset = paginated ? 0 : (-scrollElement.scrollLeft); + yOffset = paginated ? 0 : (bodyRect.top); + } + let foundHighlight; + let foundElement; + let foundRect; + for (let i = _highlights.length - 1; i >= 0; i--) { + const highlight = _highlights[i]; + let highlightParent = document.getElementById(`${highlight.id}`); + if (!highlightParent) { + highlightParent = _highlightsContainer.querySelector(`#${highlight.id}`); + } + if (!highlightParent) { + continue; + } + let hit = false; + const highlightFragments = highlightParent.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`); + for (const highlightFragment of highlightFragments) { + const withRect = highlightFragment; + const left = withRect.rect.left + xOffset; + const top = withRect.rect.top + yOffset; + foundRect = withRect.rect + if (x >= left && + x < (left + withRect.rect.width) && + y >= top && + y < (top + withRect.rect.height)) { + + + hit = true; + break; + } + } + if (hit) { + foundHighlight = highlight; + foundElement = highlightParent; + break; + } + } + + if (!foundHighlight || !foundElement) { + const highlightBoundings = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + for (const highlightBounding of highlightBoundings) { + resetHighlightBoundingStyle(win, highlightBounding); + } + const allHighlightAreas = Array.from(_highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`)); + for (const highlightArea of allHighlightAreas) { + resetHighlightAreaStyle(win, highlightArea); + } + return; + } + + if (foundElement.getAttribute("data-click")) { + if (ev.type === "mousemove") { + const foundElementHighlightAreas = Array.from(foundElement.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`)); + const allHighlightAreas = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_AREA}`); + for (const highlightArea of allHighlightAreas) { + if (foundElementHighlightAreas.indexOf(highlightArea) < 0) { + resetHighlightAreaStyle(win, highlightArea); + } + } + setHighlightAreaStyle(win, foundElementHighlightAreas, foundHighlight); + const foundElementHighlightBounding = foundElement.querySelector(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + const allHighlightBoundings = _highlightsContainer.querySelectorAll(`.${CLASS_HIGHLIGHT_BOUNDING_AREA}`); + for (const highlightBounding of allHighlightBoundings) { + if (!foundElementHighlightBounding || highlightBounding !== foundElementHighlightBounding) { + resetHighlightBoundingStyle(win, highlightBounding); + } + } + if (foundElementHighlightBounding) { + if (DEBUG_VISUALS) { + setHighlightBoundingStyle(win, foundElementHighlightBounding, foundHighlight); + } + } + } + else if (ev.type === "mouseup" || ev.type === "touchend") { + const touchedPosition = { + screenWidth: window.outerWidth, + screenHeight: window.innerHeight, + left: foundRect.left, + width: foundRect.width, + top: foundRect.top, + height: foundRect.height + }; + + const payload = { + highlight: foundHighlight, + position: touchedPosition + }; + + if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') { + electron_1.ipcRenderer.sendToHost(R2_EVENT_HIGHLIGHT_CLICK, payload); + } else if (window.webkitURL) { + if ( foundHighlight.id.search("R2_ANNOTATION_") >= 0 ) { + if (navigator.userAgent.match(/Android/i)) { + Android.highlightAnnotationMarkActivated(foundHighlight.id); + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + webkit.messageHandlers.highlightAnnotationMarkActivated.postMessage(foundHighlight.id); + } + } else if ( foundHighlight.id.search("R2_HIGHLIGHT_") >= 0 ) { + if (navigator.userAgent.match(/Android/i)) { + Android.highlightActivated(foundHighlight.id); + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + webkit.messageHandlers.highlightActivated.postMessage(foundHighlight.id); + } + } + } + + ev.stopPropagation(); + + } + } +} + +function rectsTouchOrOverlap(rect1, rect2, tolerance) { + return ((rect1.left < rect2.right || (tolerance >= 0 && almostEqual(rect1.left, rect2.right, tolerance))) && + (rect2.left < rect1.right || (tolerance >= 0 && almostEqual(rect2.left, rect1.right, tolerance))) && + (rect1.top < rect2.bottom || (tolerance >= 0 && almostEqual(rect1.top, rect2.bottom, tolerance))) && + (rect2.top < rect1.bottom || (tolerance >= 0 && almostEqual(rect2.top, rect1.bottom, tolerance)))); +} + +function replaceOverlapingRects(rects) { + for (let i = 0; i < rects.length; i++) { + for (let j = i + 1; j < rects.length; j++) { + const rect1 = rects[i]; + const rect2 = rects[j]; + if (rect1 === rect2) { + if (IS_DEV) { + console.log("replaceOverlapingRects rect1 === rect2 ??!"); + } + continue; + } + if (rectsTouchOrOverlap(rect1, rect2, -1)) { + let toAdd = []; + let toRemove; + let toPreserve; + const subtractRects1 = rectSubtract(rect1, rect2); + if (subtractRects1.length === 1) { + toAdd = subtractRects1; + toRemove = rect1; + toPreserve = rect2; + } + else { + const subtractRects2 = rectSubtract(rect2, rect1); + if (subtractRects1.length < subtractRects2.length) { + toAdd = subtractRects1; + toRemove = rect1; + toPreserve = rect2; + } + else { + toAdd = subtractRects2; + toRemove = rect2; + toPreserve = rect1; + } + } + if (IS_DEV) { + const toCheck = []; + toCheck.push(toPreserve); + Array.prototype.push.apply(toCheck, toAdd); + checkOverlaps(toCheck); + } + if (IS_DEV) { + console.log(`CLIENT RECT: overlap, cut one rect into ${toAdd.length}`); + } + const newRects = rects.filter((rect) => { + return rect !== toRemove; + }); + Array.prototype.push.apply(newRects, toAdd); + return replaceOverlapingRects(newRects); + } + } + } + return rects; +} + +function checkOverlaps(rects) { + const stillOverlapingRects = []; + for (const rect1 of rects) { + for (const rect2 of rects) { + if (rect1 === rect2) { + continue; + } + const has1 = stillOverlapingRects.indexOf(rect1) >= 0; + const has2 = stillOverlapingRects.indexOf(rect2) >= 0; + if (!has1 || !has2) { + if (rectsTouchOrOverlap(rect1, rect2, -1)) { + if (!has1) { + stillOverlapingRects.push(rect1); + } + if (!has2) { + stillOverlapingRects.push(rect2); + } + console.log("CLIENT RECT: overlap ---"); + console.log(`#1 TOP:${rect1.top} BOTTOM:${rect1.bottom} LEFT:${rect1.left} RIGHT:${rect1.right} WIDTH:${rect1.width} HEIGHT:${rect1.height}`); + console.log(`#2 TOP:${rect2.top} BOTTOM:${rect2.bottom} LEFT:${rect2.left} RIGHT:${rect2.right} WIDTH:${rect2.width} HEIGHT:${rect2.height}`); + const xOverlap = getRectOverlapX(rect1, rect2); + console.log(`xOverlap: ${xOverlap}`); + const yOverlap = getRectOverlapY(rect1, rect2); + console.log(`yOverlap: ${yOverlap}`); + } + } + } + } + if (stillOverlapingRects.length) { + console.log(`CLIENT RECT: overlaps ${stillOverlapingRects.length}`); + } +} + + +function rectContainsPoint(rect, x, y, tolerance) { + return (rect.left < x || almostEqual(rect.left, x, tolerance)) && + (rect.right > x || almostEqual(rect.right, x, tolerance)) && + (rect.top < y || almostEqual(rect.top, y, tolerance)) && + (rect.bottom > y || almostEqual(rect.bottom, y, tolerance)); +} + +function rectContains(rect1, rect2, tolerance) { + return (rectContainsPoint(rect1, rect2.left, rect2.top, tolerance) && + rectContainsPoint(rect1, rect2.right, rect2.top, tolerance) && + rectContainsPoint(rect1, rect2.left, rect2.bottom, tolerance) && + rectContainsPoint(rect1, rect2.right, rect2.bottom, tolerance)); +} + +function removeContainedRects(rects, tolerance) { + const rectsToKeep = new Set(rects); + for (const rect of rects) { + const bigEnough = rect.width > 1 && rect.height > 1; + if (!bigEnough) { + if (IS_DEV) { + console.log("CLIENT RECT: remove tiny"); + } + rectsToKeep.delete(rect); + continue; + } + for (const possiblyContainingRect of rects) { + if (rect === possiblyContainingRect) { + continue; + } + if (!rectsToKeep.has(possiblyContainingRect)) { + continue; + } + if (rectContains(possiblyContainingRect, rect, tolerance)) { + if (IS_DEV) { + console.log("CLIENT RECT: remove contained"); + } + rectsToKeep.delete(rect); + break; + } + } + } + return Array.from(rectsToKeep); +} + +function almostEqual(a, b, tolerance) { + return Math.abs(a - b) <= tolerance; +} + +function rectIntersect(rect1, rect2) { + const maxLeft = Math.max(rect1.left, rect2.left); + const minRight = Math.min(rect1.right, rect2.right); + const maxTop = Math.max(rect1.top, rect2.top); + const minBottom = Math.min(rect1.bottom, rect2.bottom); + const rect = { + bottom: minBottom, + height: Math.max(0, minBottom - maxTop), + left: maxLeft, + right: minRight, + top: maxTop, + width: Math.max(0, minRight - maxLeft), + }; + return rect; +} + +function rectSubtract(rect1, rect2) { + const rectIntersected = rectIntersect(rect2, rect1); + if (rectIntersected.height === 0 || rectIntersected.width === 0) { + return [rect1]; + } + const rects = []; + { + const rectA = { + bottom: rect1.bottom, + height: 0, + left: rect1.left, + right: rectIntersected.left, + top: rect1.top, + width: 0, + }; + rectA.width = rectA.right - rectA.left; + rectA.height = rectA.bottom - rectA.top; + if (rectA.height !== 0 && rectA.width !== 0) { + rects.push(rectA); + } + } + { + const rectB = { + bottom: rectIntersected.top, + height: 0, + left: rectIntersected.left, + right: rectIntersected.right, + top: rect1.top, + width: 0, + }; + rectB.width = rectB.right - rectB.left; + rectB.height = rectB.bottom - rectB.top; + if (rectB.height !== 0 && rectB.width !== 0) { + rects.push(rectB); + } + } + { + const rectC = { + bottom: rect1.bottom, + height: 0, + left: rectIntersected.left, + right: rectIntersected.right, + top: rectIntersected.bottom, + width: 0, + }; + rectC.width = rectC.right - rectC.left; + rectC.height = rectC.bottom - rectC.top; + if (rectC.height !== 0 && rectC.width !== 0) { + rects.push(rectC); + } + } + { + const rectD = { + bottom: rect1.bottom, + height: 0, + left: rectIntersected.right, + right: rect1.right, + top: rect1.top, + width: 0, + }; + rectD.width = rectD.right - rectD.left; + rectD.height = rectD.bottom - rectD.top; + if (rectD.height !== 0 && rectD.width !== 0) { + rects.push(rectD); + } + } + return rects; +} + +function rectContainsPoint(rect, x, y, tolerance) { + return (rect.left < x || almostEqual(rect.left, x, tolerance)) && + (rect.right > x || almostEqual(rect.right, x, tolerance)) && + (rect.top < y || almostEqual(rect.top, y, tolerance)) && + (rect.bottom > y || almostEqual(rect.bottom, y, tolerance)); +} + +function rectContains(rect1, rect2, tolerance) { + return (rectContainsPoint(rect1, rect2.left, rect2.top, tolerance) && + rectContainsPoint(rect1, rect2.right, rect2.top, tolerance) && + rectContainsPoint(rect1, rect2.left, rect2.bottom, tolerance) && + rectContainsPoint(rect1, rect2.right, rect2.bottom, tolerance)); +} + +function getBoundingRect(rect1, rect2) { + const left = Math.min(rect1.left, rect2.left); + const right = Math.max(rect1.right, rect2.right); + const top = Math.min(rect1.top, rect2.top); + const bottom = Math.max(rect1.bottom, rect2.bottom); + return { + bottom, + height: bottom - top, + left, + right, + top, + width: right - left, + }; +} + +function mergeTouchingRects(rects, tolerance, doNotMergeHorizontallyAlignedRects) { + for (let i = 0; i < rects.length; i++) { + for (let j = i + 1; j < rects.length; j++) { + const rect1 = rects[i]; + const rect2 = rects[j]; + if (rect1 === rect2) { + if (IS_DEV) { + console.log("mergeTouchingRects rect1 === rect2 ??!"); + } + continue; + } + const rectsLineUpVertically = almostEqual(rect1.top, rect2.top, tolerance) && + almostEqual(rect1.bottom, rect2.bottom, tolerance); + const rectsLineUpHorizontally = almostEqual(rect1.left, rect2.left, tolerance) && + almostEqual(rect1.right, rect2.right, tolerance); + const horizontalAllowed = !doNotMergeHorizontallyAlignedRects; + const aligned = (rectsLineUpHorizontally && horizontalAllowed) || (rectsLineUpVertically && !rectsLineUpHorizontally); + const canMerge = aligned && rectsTouchOrOverlap(rect1, rect2, tolerance); + if (canMerge) { + if (IS_DEV) { + console.log(`CLIENT RECT: merging two into one, VERTICAL: ${rectsLineUpVertically} HORIZONTAL: ${rectsLineUpHorizontally} (${doNotMergeHorizontallyAlignedRects})`); + } + const newRects = rects.filter((rect) => { + return rect !== rect1 && rect !== rect2; + }); + const replacementClientRect = getBoundingRect(rect1, rect2); + newRects.push(replacementClientRect); + return mergeTouchingRects(newRects, tolerance, doNotMergeHorizontallyAlignedRects); + } + } + } + return rects; +} + +function getClientRectsNoOverlap(range, doNotMergeHorizontallyAlignedRects) { + const rangeClientRects = range.getClientRects(); + return getClientRectsNoOverlap_(rangeClientRects, doNotMergeHorizontallyAlignedRects); +} + +function getClientRectsNoOverlap_(clientRects, doNotMergeHorizontallyAlignedRects) { + const tolerance = 1; + const originalRects = []; + for (const rangeClientRect of clientRects) { + originalRects.push({ + bottom: rangeClientRect.bottom, + height: rangeClientRect.height, + left: rangeClientRect.left, + right: rangeClientRect.right, + top: rangeClientRect.top, + width: rangeClientRect.width, + }); + } + const mergedRects = mergeTouchingRects(originalRects, tolerance, doNotMergeHorizontallyAlignedRects); + const noContainedRects = removeContainedRects(mergedRects, tolerance); + const newRects = replaceOverlapingRects(noContainedRects); + const minArea = 2 * 2; + for (let j = newRects.length - 1; j >= 0; j--) { + const rect = newRects[j]; + const bigEnough = (rect.width * rect.height) > minArea; + if (!bigEnough) { + if (newRects.length > 1) { + if (IS_DEV) { + console.log("CLIENT RECT: remove small"); + } + newRects.splice(j, 1); + } + else { + if (IS_DEV) { + console.log("CLIENT RECT: remove small, but keep otherwise empty!"); + } + break; + } + } + } + if (IS_DEV) { + checkOverlaps(newRects); + } + if (IS_DEV) { + console.log(`CLIENT RECT: reduced ${originalRects.length} --> ${newRects.length}`); + } + return newRects; +} + +function isPaginated(document) { + return document && document.documentElement && + document.documentElement.classList.contains(CLASS_PAGINATED); +} + +function getScrollingElement(document){ + if (document.scrollingElement) { + return document.scrollingElement; + } + return document.body; +}; + + +function ensureContainer(win, annotationFlag) { + const document = win.document; + + if (!_highlightsContainer) { + if (!bodyEventListenersSet) { + bodyEventListenersSet = true; + document.body.addEventListener("mousedown", (ev) => { + lastMouseDownX = ev.clientX; + lastMouseDownY = ev.clientY; + }, false); + document.body.addEventListener("mouseup", (ev) => { + if ((Math.abs(lastMouseDownX - ev.clientX) < 3) && + (Math.abs(lastMouseDownY - ev.clientY) < 3)) { + processMouseEvent(win, ev); + } + }, false); + document.body.addEventListener("mousemove", (ev) => { + processMouseEvent(win, ev); + }, false); + + document.body.addEventListener("touchend", function touchEnd(e) {processTouchEvent(win, e)}, false); + } + _highlightsContainer = document.createElement("div"); + _highlightsContainer.setAttribute("id", ID_HIGHLIGHTS_CONTAINER); + + _highlightsContainer.style.setProperty("pointer-events", "none"); + document.body.append(_highlightsContainer); + } + + return _highlightsContainer; +} + +function hideAllhighlights() { + if (_highlightsContainer) { + _highlightsContainer.remove(); + _highlightsContainer = null; + } +} + +function destroyAllhighlights() { + hideAllhighlights(); + _highlights.splice(0, _highlights.length); +} + +function destroyHighlight(id) { + let i = -1; + let _document = window.document + const highlight = _highlights.find((h, j) => { + i = j; + return h.id === id; + }); + if (highlight && i >= 0 && i < _highlights.length) { + _highlights.splice(i, 1); + } + const highlightContainer = _document.getElementById(id); + if (highlightContainer) { + highlightContainer.remove(); + } +} + +function isCfiTextNode(node) { + return node.nodeType !== Node.ELEMENT_NODE; +} + +function getChildTextNodeCfiIndex(element, child) { + let found = -1; + let textNodeIndex = -1; + let previousWasElement = false; + for (let i = 0; i < element.childNodes.length; i++) { + const childNode = element.childNodes[i]; + const isText = isCfiTextNode(childNode); + if (isText || previousWasElement) { + textNodeIndex += 2; + } + if (isText) { + if (childNode === child) { + found = textNodeIndex; + break; + } + } + previousWasElement = childNode.nodeType === Node.ELEMENT_NODE; + } + return found; +} + +function getCommonAncestorElement(node1, node2) { + if (node1.nodeType === Node.ELEMENT_NODE && node1 === node2) { + return node1; + } + if (node1.nodeType === Node.ELEMENT_NODE && node1.contains(node2)) { + return node1; + } + if (node2.nodeType === Node.ELEMENT_NODE && node2.contains(node1)) { + return node2; + } + const node1ElementAncestorChain = []; + let parent = node1.parentNode; + while (parent && parent.nodeType === Node.ELEMENT_NODE) { + node1ElementAncestorChain.push(parent); + parent = parent.parentNode; + } + const node2ElementAncestorChain = []; + parent = node2.parentNode; + while (parent && parent.nodeType === Node.ELEMENT_NODE) { + node2ElementAncestorChain.push(parent); + parent = parent.parentNode; + } + let commonAncestor = node1ElementAncestorChain.find((node1ElementAncestor) => { + return node2ElementAncestorChain.indexOf(node1ElementAncestor) >= 0; + }); + if (!commonAncestor) { + commonAncestor = node2ElementAncestorChain.find((node2ElementAncestor) => { + return node1ElementAncestorChain.indexOf(node2ElementAncestor) >= 0; + }); + } + return commonAncestor; +} + +function fullQualifiedSelector(node) { + if (node.nodeType !== Node.ELEMENT_NODE) { + const lowerCaseName = (node.localName && node.localName.toLowerCase()) + || node.nodeName.toLowerCase(); + return lowerCaseName; + } + //return cssPath(node, justSelector); + return cssPath(node, true); +}; + +function getCurrentSelectionInfo() { + const selection = window.getSelection(); + if (!selection) { + return undefined; + } + if (selection.isCollapsed) { + console.log("^^^ SELECTION COLLAPSED."); + return undefined; + } + const rawText = selection.toString(); + const cleanText = rawText.trim().replace(/\n/g, " ").replace(/\s\s+/g, " "); + if (cleanText.length === 0) { + console.log("^^^ SELECTION TEXT EMPTY."); + return undefined; + } + if (!selection.anchorNode || !selection.focusNode) { + return undefined; + } + const range = selection.rangeCount === 1 ? selection.getRangeAt(0) : + createOrderedRange(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset); + if (!range || range.collapsed) { + console.log("$$$$$$$$$$$$$$$$$ CANNOT GET NON-COLLAPSED SELECTION RANGE?!"); + return undefined; + } + const rangeInfo = convertRange(range, fullQualifiedSelector, computeCFI); + if (!rangeInfo) { + console.log("^^^ SELECTION RANGE INFO FAIL?!"); + return undefined; + } + + if (IS_DEV && DEBUG_VISUALS) { + const restoredRange = convertRangeInfo(win.document, rangeInfo); + if (restoredRange) { + if (restoredRange.startOffset === range.startOffset && + restoredRange.endOffset === range.endOffset && + restoredRange.startContainer === range.startContainer && + restoredRange.endContainer === range.endContainer) { + console.log("SELECTION RANGE RESTORED OKAY (dev check)."); + } + else { + console.log("SELECTION RANGE RESTORE FAIL (dev check)."); + dumpDebug("SELECTION", selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset, getCssSelector); + dumpDebug("ORDERED RANGE FROM SELECTION", range.startContainer, range.startOffset, range.endContainer, range.endOffset, getCssSelector); + dumpDebug("RESTORED RANGE", restoredRange.startContainer, restoredRange.startOffset, restoredRange.endContainer, restoredRange.endOffset, getCssSelector); + } + } + else { + console.log("CANNOT RESTORE SELECTION RANGE ??!"); + } + } + else { + } + + return { + locations: rangeInfo2Location(rangeInfo), + text: { + highlight: rawText + } + }; +} + +function checkBlacklisted(el) { + + let blacklistedId; + const id = el.getAttribute("id"); + if (id && _blacklistIdClassForCFI.indexOf(id) >= 0) { + console.log("checkBlacklisted ID: " + id); + blacklistedId = id; + } + let blacklistedClass; + for (const item of _blacklistIdClassForCFI) { + if (el.classList.contains(item)) { + console.log("checkBlacklisted CLASS: " + item); + blacklistedClass = item; + break; + } + } + if (blacklistedId || blacklistedClass) { + return true; + } + + return false; +}; + +function cssPath (node, optimized){ + if (node.nodeType !== Node.ELEMENT_NODE) { + return ""; + } + + const steps = []; + let contextNode = node; + while (contextNode) { + const step = _cssPathStep(contextNode, !!optimized, contextNode === node); + if (!step) { + break; // Error - bail out early. + } + steps.push(step.value); + if (step.optimized) { + break; + } + contextNode = contextNode.parentNode; + } + steps.reverse(); + return steps.join(" > "); +}; +// tslint:disable-next-line:max-line-length +// https://chromium.googlesource.com/chromium/blink/+/master/Source/devtools/front_end/components/DOMPresentationUtils.js#316 +function _cssPathStep(node, optimized, isTargetNode) { + + function prefixedElementClassNames (nd) { + const classAttribute = nd.getAttribute("class"); + if (!classAttribute) { + return []; + } + + return classAttribute.split(/\s+/g).filter(Boolean).map((nm) => { + // The prefix is required to store "__proto__" in a object-based map. + return "$" + nm; + }); + }; + + function idSelector (idd) { + return "#" + escapeIdentifierIfNeeded(idd); + }; + + function escapeIdentifierIfNeeded(ident) { + if (isCSSIdentifier(ident)) { + return ident; + } + + const shouldEscapeFirst = /^(?:[0-9]|-[0-9-]?)/.test(ident); + const lastIndex = ident.length - 1; + return ident.replace(/./g, function(c, ii) { + return ((shouldEscapeFirst && ii === 0) || !isCSSIdentChar(c)) ? escapeAsciiChar(c, ii === lastIndex) : c; + }); + }; + + function escapeAsciiChar(c, isLast){ + return "\\" + toHexByte(c) + (isLast ? "" : " "); + }; + + function toHexByte (c){ + let hexByte = c.charCodeAt(0).toString(16); + if (hexByte.length === 1) { + hexByte = "0" + hexByte; + } + return hexByte; + }; + + function isCSSIdentChar (c) { + if (/[a-zA-Z0-9_-]/.test(c)) { + return true; + } + return c.charCodeAt(0) >= 0xA0; + }; + + function isCSSIdentifier (value){ + return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value); + }; + + if (node.nodeType !== Node.ELEMENT_NODE) { + return undefined; + } + const lowerCaseName = (node.localName && node.localName.toLowerCase()) + || node.nodeName.toLowerCase(); + + const element = node; + + const id = element.getAttribute("id"); + + if (optimized) { + if (id) { + return { + optimized: true, + value: idSelector(id), + }; + } + if (lowerCaseName === "body" || lowerCaseName === "head" || lowerCaseName === "html") { + return { + optimized: true, + value: lowerCaseName, // node.nodeNameInCorrectCase(), + }; + } + } + + const nodeName = lowerCaseName; // node.nodeNameInCorrectCase(); + if (id) { + return { + optimized: true, + value: nodeName + idSelector(id), + }; + } + + const parent = node.parentNode; + + if (!parent || parent.nodeType === Node.DOCUMENT_NODE) { + return { + optimized: true, + value: nodeName, + }; + } + + const prefixedOwnClassNamesArray_ = prefixedElementClassNames(element); + + const prefixedOwnClassNamesArray = []; // .keySet() + prefixedOwnClassNamesArray_.forEach((arrItem) => { + if (prefixedOwnClassNamesArray.indexOf(arrItem) < 0) { + prefixedOwnClassNamesArray.push(arrItem); + } + }); + + + let needsClassNames = false; + let needsNthChild = false; + let ownIndex = -1; + let elementIndex = -1; + const siblings = parent.children; + + for (let i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.length; ++i) { + const sibling = siblings[i]; + if (sibling.nodeType !== Node.ELEMENT_NODE) { + continue; + } + elementIndex += 1; + if (sibling === node) { + ownIndex = elementIndex; + continue; + } + if (needsNthChild) { + continue; + } + + // sibling.nodeNameInCorrectCase() + const siblingName = (sibling.localName && sibling.localName.toLowerCase()) || sibling.nodeName.toLowerCase(); + if (siblingName !== nodeName) { + continue; + } + needsClassNames = true; + + const ownClassNames = []; + prefixedOwnClassNamesArray.forEach((arrItem) => { + ownClassNames.push(arrItem); + }); + let ownClassNameCount = ownClassNames.length; + + if (ownClassNameCount === 0) { + needsNthChild = true; + continue; + } + const siblingClassNamesArray_ = prefixedElementClassNames(sibling); + const siblingClassNamesArray = []; // .keySet() + siblingClassNamesArray_.forEach((arrItem) => { + if (siblingClassNamesArray.indexOf(arrItem) < 0) { + siblingClassNamesArray.push(arrItem); + } + }); + + for (const siblingClass of siblingClassNamesArray) { + const ind = ownClassNames.indexOf(siblingClass); + if (ind < 0) { + continue; + } + + ownClassNames.splice(ind, 1); // delete ownClassNames[siblingClass]; + + if (!--ownClassNameCount) { + needsNthChild = true; + break; + } + } + } + + let result = nodeName; + if (isTargetNode && + nodeName === "input" && + element.getAttribute("type") && + !element.getAttribute("id") && + !element.getAttribute("class")) { + result += "[type=\"" + element.getAttribute("type") + "\"]"; + } + if (needsNthChild) { + result += ":nth-child(" + (ownIndex + 1) + ")"; + } else if (needsClassNames) { + for (const prefixedName of prefixedOwnClassNamesArray) { + result += "." + escapeIdentifierIfNeeded(prefixedName.substr(1)); + } + } + + return { + optimized: false, + value: result, + }; +}; + +function computeCFI (node) { + + // TODO: handle character position inside text node + if (node.nodeType !== Node.ELEMENT_NODE) { + return undefined; + } + + let cfi = ""; + + let currentElement = node; + while (currentElement.parentNode && currentElement.parentNode.nodeType === Node.ELEMENT_NODE) { + const blacklisted = checkBlacklisted(currentElement); + if (!blacklisted) { + const currentElementParentChildren = currentElement.parentNode.children; + let currentElementIndex = -1; + for (let i = 0; i < currentElementParentChildren.length; i++) { + if (currentElement === currentElementParentChildren[i]) { + currentElementIndex = i; + break; + } + } + if (currentElementIndex >= 0) { + const cfiIndex = (currentElementIndex + 1) * 2; + cfi = cfiIndex + + (currentElement.id ? ("[" + currentElement.id + "]") : "") + + (cfi.length ? ("/" + cfi) : ""); + } + } + currentElement = currentElement.parentNode; + } + + return "/" + cfi; +}; + +function _createHighlight(locations, color, pointerInteraction, type) { + const rangeInfo = location2RangeInfo(locations) + const uniqueStr = `${rangeInfo.cfi}${rangeInfo.startContainerElementCssSelector}${rangeInfo.startContainerChildTextNodeIndex}${rangeInfo.startOffset}${rangeInfo.endContainerElementCssSelector}${rangeInfo.endContainerChildTextNodeIndex}${rangeInfo.endOffset}`; + + const sha256Hex = CryptoJS.SHA256(uniqueStr).toString(CryptoJS.enc.Hex) + + var id; + if ( type == ID_HIGHLIGHTS_CONTAINER ) { + id = "R2_HIGHLIGHT_" + sha256Hex; + } else { + id = "R2_ANNOTATION_" + sha256Hex; + } + + destroyHighlight(id); + + const highlight = { + color: color ? color : DEFAULT_BACKGROUND_COLOR, + id, + pointerInteraction, + rangeInfo + }; + _highlights.push(highlight); + createHighlightDom(window, highlight, (type == ID_ANNOTATION_CONTAINER)? true : false); + + return highlight; +} + +function createHighlight(selectionInfo, color, pointerInteraction) { + return _createHighlight(selectionInfo, color, pointerInteraction, ID_HIGHLIGHTS_CONTAINER) +} + +function createAnnotation(selectionInfo, color, pointerInteraction) { + return _createHighlight(selectionInfo, color, pointerInteraction, ID_ANNOTATION_CONTAINER) +} + +function createAnnotation(id) { + let i = -1; + + const highlight = _highlights.find((h, j) => { + i = j; + return h.id === id; + }); + if ( i == _highlights.length ) + return + + var locations = { + locations: rangeInfo2Location(highlight.rangeInfo) + } + + return _createHighlight(locations, highlight.color, true, ID_ANNOTATION_CONTAINER) +} + + +function createHighlightDom(win, highlight, annotationFlag) { + + const document = win.document; + + const scale = 1 / ((win.READIUM2 && win.READIUM2.isFixedLayout) ? win.READIUM2.fxlViewportScale : 1); + + const scrollElement = getScrollingElement(document); + + const range = convertRangeInfo(document, highlight.rangeInfo); + if (!range) { + return undefined; + } + + const paginated = isPaginated(document); + const highlightsContainer = ensureContainer(win, annotationFlag); + const highlightParent = document.createElement("div"); + + highlightParent.setAttribute("id", highlight.id); + highlightParent.setAttribute("class", CLASS_HIGHLIGHT_CONTAINER); + + document.body.style.position = "relative"; + highlightParent.style.setProperty("pointer-events", "none"); + if (highlight.pointerInteraction) { + highlightParent.setAttribute("data-click", "1"); + } + + const bodyRect = document.body.getBoundingClientRect(); + const useSVG = !DEBUG_VISUALS && USE_SVG; + //const useSVG = USE_SVG; + const drawUnderline = false; + const drawStrikeThrough = false; + const doNotMergeHorizontallyAlignedRects = drawUnderline || drawStrikeThrough; + //const clientRects = DEBUG_VISUALS ? range.getClientRects() : + const clientRects = getClientRectsNoOverlap(range, doNotMergeHorizontallyAlignedRects); + let highlightAreaSVGDocFrag; + const roundedCorner = 3; + const underlineThickness = 2; + const strikeThroughLineThickness = 3; + const opacity = DEFAULT_BACKGROUND_COLOR_OPACITY; + let extra = ""; + const rangeAnnotationBoundingClientRect = frameForHighlightAnnotationMarkWithID(win, highlight.id); + + let xOffset; + let yOffset; + let annotationOffset; + + if (navigator.userAgent.match(/Android/i)) { + xOffset = paginated ? (-scrollElement.scrollLeft) : bodyRect.left; + yOffset = paginated ? (-scrollElement.scrollTop) : bodyRect.top; + annotationOffset = parseInt((rangeAnnotationBoundingClientRect.right - xOffset)/ window.innerWidth) + 1; + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + xOffset = paginated ? 0 : (-scrollElement.scrollLeft); + yOffset = paginated ? 0 : (bodyRect.top); + annotationOffset = parseInt((rangeAnnotationBoundingClientRect.right/window.innerWidth) + 1); + } + + for (const clientRect of clientRects) { + if (useSVG) { + const borderThickness = 0; + if (!highlightAreaSVGDocFrag) { + highlightAreaSVGDocFrag = document.createDocumentFragment(); + } + const highlightAreaSVGRect = document.createElementNS(SVG_XML_NAMESPACE, "rect"); + + highlightAreaSVGRect.setAttribute("class", CLASS_HIGHLIGHT_AREA); + highlightAreaSVGRect.setAttribute("style", `fill: rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}) !important; fill-opacity: ${opacity} !important; stroke-width: 0;`); + highlightAreaSVGRect.scale = scale; + + /* + highlightAreaSVGRect.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width, + }; + */ + + if (annotationFlag) { + highlightAreaSVGRect.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + highlightAreaSVGRect.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width + }; + } + + highlightAreaSVGRect.setAttribute("rx", `${roundedCorner * scale}`); + highlightAreaSVGRect.setAttribute("ry", `${roundedCorner * scale}`); + highlightAreaSVGRect.setAttribute("x", `${(highlightAreaSVGRect.rect.left - borderThickness) * scale}`); + highlightAreaSVGRect.setAttribute("y", `${(highlightAreaSVGRect.rect.top - borderThickness) * scale}`); + highlightAreaSVGRect.setAttribute("height", `${(highlightAreaSVGRect.rect.height + (borderThickness * 2)) * scale}`); + highlightAreaSVGRect.setAttribute("width", `${(highlightAreaSVGRect.rect.width + (borderThickness * 2)) * scale}`); + highlightAreaSVGDocFrag.appendChild(highlightAreaSVGRect); + if (drawUnderline) { + const highlightAreaSVGLine = document.createElementNS(SVG_XML_NAMESPACE, "line"); + highlightAreaSVGRect.setAttribute("class", CLASS_HIGHLIGHT_AREA); + highlightAreaSVGLine.setAttribute("style", `stroke-linecap: round; stroke-width: ${underlineThickness * scale}; stroke: rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}) !important; stroke-opacity: ${opacity} !important`); + highlightAreaSVGLine.scale = scale; + /* + highlightAreaSVGLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width, + }; + */ + if (annotationFlag) { + highlightAreaSVGLine.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + highlightAreaSVGLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width + }; + } + + const lineOffset = (highlightAreaSVGLine.rect.width > roundedCorner) ? roundedCorner : 0; + highlightAreaSVGLine.setAttribute("x1", `${(highlightAreaSVGLine.rect.left + lineOffset) * scale}`); + highlightAreaSVGLine.setAttribute("x2", `${(highlightAreaSVGLine.rect.left + highlightAreaSVGLine.rect.width - lineOffset) * scale}`); + const y = (highlightAreaSVGLine.rect.top + highlightAreaSVGLine.rect.height - (underlineThickness / 2)) * scale; + highlightAreaSVGLine.setAttribute("y1", `${y}`); + highlightAreaSVGLine.setAttribute("y2", `${y}`); + highlightAreaSVGLine.setAttribute("height", `${highlightAreaSVGLine.rect.height * scale}`); + highlightAreaSVGLine.setAttribute("width", `${highlightAreaSVGLine.rect.width * scale}`); + highlightAreaSVGDocFrag.appendChild(highlightAreaSVGLine); + } + if (drawStrikeThrough) { + const highlightAreaSVGLine = document.createElementNS(SVG_XML_NAMESPACE, "line"); + + highlightAreaSVGRect.setAttribute("class", CLASS_HIGHLIGHT_AREA); + highlightAreaSVGLine.setAttribute("style", `stroke-linecap: butt; stroke-width: ${strikeThroughLineThickness * scale}; stroke: rgb(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}) !important; stroke-opacity: ${opacity} !important`); + highlightAreaSVGLine.scale = scale; + + /* + highlightAreaSVGLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width, + }; + */ + + if (annotationFlag) { + highlightAreaSVGLine.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + highlightAreaSVGLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width + }; + } + + highlightAreaSVGLine.setAttribute("x1", `${highlightAreaSVGLine.rect.left * scale}`); + highlightAreaSVGLine.setAttribute("x2", `${(highlightAreaSVGLine.rect.left + highlightAreaSVGLine.rect.width) * scale}`); + const lineOffset = highlightAreaSVGLine.rect.height / 2; + const y = (highlightAreaSVGLine.rect.top + lineOffset) * scale; + highlightAreaSVGLine.setAttribute("y1", `${y}`); + highlightAreaSVGLine.setAttribute("y2", `${y}`); + highlightAreaSVGLine.setAttribute("height", `${highlightAreaSVGLine.rect.height * scale}`); + highlightAreaSVGLine.setAttribute("width", `${highlightAreaSVGLine.rect.width * scale}`); + highlightAreaSVGDocFrag.appendChild(highlightAreaSVGLine); + } + } + else { + const highlightArea = document.createElement("div"); + + highlightArea.setAttribute("class", CLASS_HIGHLIGHT_AREA); + + if (DEBUG_VISUALS) { + const rgb = Math.round(0xffffff * Math.random()); + const r = rgb >> 16; + const g = rgb >> 8 & 255; + const b = rgb & 255; + extra = `outline-color: rgb(${r}, ${g}, ${b}); outline-style: solid; outline-width: 1px; outline-offset: -1px;`; + } + else { + + if (drawUnderline) { + extra += `border-bottom: ${underlineThickness * scale}px solid rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity}) !important`; + } + } + highlightArea.setAttribute("style", `border-radius: ${roundedCorner}px !important; background-color: rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity}) !important; ${extra}`); + highlightArea.style.setProperty("pointer-events", "none"); + highlightArea.style.position = paginated ? "fixed" : "absolute"; + highlightArea.scale = scale; + /* + highlightArea.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width, + }; + */ + if (annotationFlag) { + highlightArea.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + highlightArea.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width + }; + } + + highlightArea.style.width = `${highlightArea.rect.width * scale}px`; + highlightArea.style.height = `${highlightArea.rect.height * scale}px`; + highlightArea.style.left = `${highlightArea.rect.left * scale}px`; + highlightArea.style.top = `${highlightArea.rect.top * scale}px`; + highlightParent.append(highlightArea); + if (!DEBUG_VISUALS && drawStrikeThrough) { + //if (drawStrikeThrough) { + const highlightAreaLine = document.createElement("div"); + highlightAreaLine.setAttribute("class", CLASS_HIGHLIGHT_AREA); + + highlightAreaLine.setAttribute("style", `background-color: rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity}) !important;`); + highlightAreaLine.style.setProperty("pointer-events", "none"); + highlightAreaLine.style.position = paginated ? "fixed" : "absolute"; + highlightAreaLine.scale = scale; + /* + highlightAreaLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width, + }; + */ + + if (annotationFlag) { + highlightAreaLine.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + highlightAreaLine.rect = { + height: clientRect.height, + left: clientRect.left - xOffset, + top: clientRect.top - yOffset, + width: clientRect.width + }; + } + + highlightAreaLine.style.width = `${highlightAreaLine.rect.width * scale}px`; + highlightAreaLine.style.height = `${strikeThroughLineThickness * scale}px`; + highlightAreaLine.style.left = `${highlightAreaLine.rect.left * scale}px`; + highlightAreaLine.style.top = `${(highlightAreaLine.rect.top + (highlightAreaLine.rect.height / 2) - (strikeThroughLineThickness / 2)) * scale}px`; + highlightParent.append(highlightAreaLine); + } + } + + if (annotationFlag) { + break; + } + } + + if (useSVG && highlightAreaSVGDocFrag) { + const highlightAreaSVG = document.createElementNS(SVG_XML_NAMESPACE, "svg"); + highlightAreaSVG.setAttribute("pointer-events", "none"); + highlightAreaSVG.style.position = paginated ? "fixed" : "absolute"; + highlightAreaSVG.style.overflow = "visible"; + highlightAreaSVG.style.left = "0"; + highlightAreaSVG.style.top = "0"; + highlightAreaSVG.append(highlightAreaSVGDocFrag); + highlightParent.append(highlightAreaSVG); + } + + const highlightBounding = document.createElement("div"); + + if ( annotationFlag ) { + highlightBounding.setAttribute("class", CLASS_ANNOTATION_BOUNDING_AREA); + highlightBounding.setAttribute("style", `border-radius: ${roundedCorner}px !important; background-color: rgba(${highlight.color.red}, ${highlight.color.green}, ${highlight.color.blue}, ${opacity}) !important; ${extra}`); + } else { + highlightBounding.setAttribute("class", CLASS_HIGHLIGHT_BOUNDING_AREA); + } + + highlightBounding.style.setProperty("pointer-events", "none"); + highlightBounding.style.position = paginated ? "fixed" : "absolute"; + highlightBounding.scale = scale; + + if (DEBUG_VISUALS) { + highlightBounding.setAttribute("style", `outline-color: magenta; outline-style: solid; outline-width: 1px; outline-offset: -1px;`); + } + + if (annotationFlag) { + highlightBounding.rect = { + height: ANNOTATION_WIDTH, //rangeAnnotationBoundingClientRect.height - rangeAnnotationBoundingClientRect.height/4, + left: window.innerWidth * annotationOffset - ANNOTATION_WIDTH, + top: rangeAnnotationBoundingClientRect.top - yOffset, + width: ANNOTATION_WIDTH + }; + } else { + const rangeBoundingClientRect = range.getBoundingClientRect(); + highlightBounding.rect = { + height: rangeBoundingClientRect.height, + left: rangeBoundingClientRect.left - xOffset, + top: rangeBoundingClientRect.top - yOffset, + width: rangeBoundingClientRect.width + }; + } + + highlightBounding.style.width = `${highlightBounding.rect.width * scale}px`; + highlightBounding.style.height = `${highlightBounding.rect.height * scale}px`; + highlightBounding.style.left = `${highlightBounding.rect.left * scale}px`; + highlightBounding.style.top = `${highlightBounding.rect.top * scale}px`; + + highlightParent.append(highlightBounding); + highlightsContainer.append(highlightParent); + + return highlightParent; +} + + +function createOrderedRange(startNode, startOffset, endNode, endOffset) { + const range = new Range(); + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + if (!range.collapsed) { + return range; + } + console.log(">>> createOrderedRange COLLAPSED ... RANGE REVERSE?"); + const rangeReverse = new Range(); + rangeReverse.setStart(endNode, endOffset); + rangeReverse.setEnd(startNode, startOffset); + if (!rangeReverse.collapsed) { + console.log(">>> createOrderedRange RANGE REVERSE OK."); + return range; + } + console.log(">>> createOrderedRange RANGE REVERSE ALSO COLLAPSED?!"); + return undefined; +} + +function convertRange(range, getCssSelector, computeElementCFI) { + const startIsElement = range.startContainer.nodeType === Node.ELEMENT_NODE; + const startContainerElement = startIsElement ? + range.startContainer : + ((range.startContainer.parentNode && range.startContainer.parentNode.nodeType === Node.ELEMENT_NODE) ? + range.startContainer.parentNode : undefined); + if (!startContainerElement) { + return undefined; + } + const startContainerChildTextNodeIndex = startIsElement ? -1 : + Array.from(startContainerElement.childNodes).indexOf(range.startContainer); + if (startContainerChildTextNodeIndex < -1) { + return undefined; + } + const startContainerElementCssSelector = getCssSelector(startContainerElement); + const endIsElement = range.endContainer.nodeType === Node.ELEMENT_NODE; + const endContainerElement = endIsElement ? + range.endContainer : + ((range.endContainer.parentNode && range.endContainer.parentNode.nodeType === Node.ELEMENT_NODE) ? + range.endContainer.parentNode : undefined); + if (!endContainerElement) { + return undefined; + } + const endContainerChildTextNodeIndex = endIsElement ? -1 : + Array.from(endContainerElement.childNodes).indexOf(range.endContainer); + if (endContainerChildTextNodeIndex < -1) { + return undefined; + } + const endContainerElementCssSelector = getCssSelector(endContainerElement); + const commonElementAncestor = getCommonAncestorElement(range.startContainer, range.endContainer); + if (!commonElementAncestor) { + console.log("^^^ NO RANGE COMMON ANCESTOR?!"); + return undefined; + } + if (range.commonAncestorContainer) { + const rangeCommonAncestorElement = range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE ? + range.commonAncestorContainer : range.commonAncestorContainer.parentNode; + if (rangeCommonAncestorElement && rangeCommonAncestorElement.nodeType === Node.ELEMENT_NODE) { + if (commonElementAncestor !== rangeCommonAncestorElement) { + console.log(">>>>>> COMMON ANCESTOR CONTAINER DIFF??!"); + console.log(getCssSelector(commonElementAncestor)); + console.log(getCssSelector(rangeCommonAncestorElement)); + } + } + } + const rootElementCfi = computeElementCFI(commonElementAncestor); + const startElementCfi = computeElementCFI(startContainerElement); + const endElementCfi = computeElementCFI(endContainerElement); + let cfi; + if (rootElementCfi && startElementCfi && endElementCfi) { + let startElementOrTextCfi = startElementCfi; + if (!startIsElement) { + const startContainerChildTextNodeIndexForCfi = getChildTextNodeCfiIndex(startContainerElement, range.startContainer); + startElementOrTextCfi = startElementCfi + "/" + + startContainerChildTextNodeIndexForCfi + ":" + range.startOffset; + } + else { + if (range.startOffset >= 0 && range.startOffset < startContainerElement.childNodes.length) { + const childNode = startContainerElement.childNodes[range.startOffset]; + if (childNode.nodeType === Node.ELEMENT_NODE) { + startElementOrTextCfi = startElementCfi + "/" + ((range.startOffset + 1) * 2); + } + else { + const cfiTextNodeIndex = getChildTextNodeCfiIndex(startContainerElement, childNode); + startElementOrTextCfi = startElementCfi + "/" + cfiTextNodeIndex; + } + } + else { + const cfiIndexOfLastElement = ((startContainerElement.childElementCount) * 2); + const lastChildNode = startContainerElement.childNodes[startContainerElement.childNodes.length - 1]; + if (lastChildNode.nodeType === Node.ELEMENT_NODE) { + startElementOrTextCfi = startElementCfi + "/" + (cfiIndexOfLastElement + 1); + } + else { + startElementOrTextCfi = startElementCfi + "/" + (cfiIndexOfLastElement + 2); + } + } + } + let endElementOrTextCfi = endElementCfi; + if (!endIsElement) { + const endContainerChildTextNodeIndexForCfi = getChildTextNodeCfiIndex(endContainerElement, range.endContainer); + endElementOrTextCfi = endElementCfi + "/" + + endContainerChildTextNodeIndexForCfi + ":" + range.endOffset; + } + else { + if (range.endOffset >= 0 && range.endOffset < endContainerElement.childNodes.length) { + const childNode = endContainerElement.childNodes[range.endOffset]; + if (childNode.nodeType === Node.ELEMENT_NODE) { + endElementOrTextCfi = endElementCfi + "/" + ((range.endOffset + 1) * 2); + } + else { + const cfiTextNodeIndex = getChildTextNodeCfiIndex(endContainerElement, childNode); + endElementOrTextCfi = endElementCfi + "/" + cfiTextNodeIndex; + } + } + else { + const cfiIndexOfLastElement = ((endContainerElement.childElementCount) * 2); + const lastChildNode = endContainerElement.childNodes[endContainerElement.childNodes.length - 1]; + if (lastChildNode.nodeType === Node.ELEMENT_NODE) { + endElementOrTextCfi = endElementCfi + "/" + (cfiIndexOfLastElement + 1); + } + else { + endElementOrTextCfi = endElementCfi + "/" + (cfiIndexOfLastElement + 2); + } + } + } + cfi = rootElementCfi + "," + + startElementOrTextCfi.replace(rootElementCfi, "") + "," + + endElementOrTextCfi.replace(rootElementCfi, ""); + } + return { + cfi, + endContainerChildTextNodeIndex, + endContainerElementCssSelector, + endOffset: range.endOffset, + startContainerChildTextNodeIndex, + startContainerElementCssSelector, + startOffset: range.startOffset, + }; +} + +function convertRangeInfo(document, rangeInfo) { + const startElement = document.querySelector(rangeInfo.startContainerElementCssSelector); + if (!startElement) { + console.log("^^^ convertRangeInfo NO START ELEMENT CSS SELECTOR?!"); + return undefined; + } + let startContainer = startElement; + if (rangeInfo.startContainerChildTextNodeIndex >= 0) { + if (rangeInfo.startContainerChildTextNodeIndex >= startElement.childNodes.length) { + console.log("^^^ convertRangeInfo rangeInfo.startContainerChildTextNodeIndex >= startElement.childNodes.length?!"); + return undefined; + } + startContainer = startElement.childNodes[rangeInfo.startContainerChildTextNodeIndex]; + if (startContainer.nodeType !== Node.TEXT_NODE) { + console.log("^^^ convertRangeInfo startContainer.nodeType !== Node.TEXT_NODE?!"); + return undefined; + } + } + const endElement = document.querySelector(rangeInfo.endContainerElementCssSelector); + if (!endElement) { + console.log("^^^ convertRangeInfo NO END ELEMENT CSS SELECTOR?!"); + return undefined; + } + let endContainer = endElement; + if (rangeInfo.endContainerChildTextNodeIndex >= 0) { + if (rangeInfo.endContainerChildTextNodeIndex >= endElement.childNodes.length) { + console.log("^^^ convertRangeInfo rangeInfo.endContainerChildTextNodeIndex >= endElement.childNodes.length?!"); + return undefined; + } + endContainer = endElement.childNodes[rangeInfo.endContainerChildTextNodeIndex]; + if (endContainer.nodeType !== Node.TEXT_NODE) { + console.log("^^^ convertRangeInfo endContainer.nodeType !== Node.TEXT_NODE?!"); + return undefined; + } + } + return createOrderedRange(startContainer, rangeInfo.startOffset, endContainer, rangeInfo.endOffset); +} + + +function frameForHighlightAnnotationMarkWithID(win, id) { + let clientRects = frameForHighlightWithID(id); + if (!clientRects) + return; + + var topClientRect = clientRects[0]; + var maxHeight = topClientRect.height; + for (const clientRect of clientRects) { + if ( clientRect.top < topClientRect.top ) + topClientRect = clientRect + if ( clientRect.height > maxHeight ) + maxHeight = clientRect.height + } + + const document = win.document; + + const scrollElement = getScrollingElement(document); + const paginated = isPaginated(document); + const bodyRect = document.body.getBoundingClientRect(); + let yOffset; + if (navigator.userAgent.match(/Android/i)) { + yOffset = paginated ? (-scrollElement.scrollTop) : bodyRect.top; + } else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { + yOffset = paginated ? 0 : (bodyRect.top); + } + var newTop = topClientRect.top; + + if (_highlightsContainer) { + do { + var boundingAreas = document.getElementsByClassName(CLASS_ANNOTATION_BOUNDING_AREA); + var found = false; + //for (let i = 0, length = boundingAreas.snapshotLength; i < length; ++i) { + for (var i=0, len=boundingAreas.length|0; i { + i = j; + return h.id === id; + }); + return highlight + +} + +function frameForHighlightWithID(id) { + + const highlight = highlightWithID(id); + if (!highlight) + return; + + const document = window.document; + const scrollElement = getScrollingElement(document); + const range = convertRangeInfo(document, highlight.rangeInfo); + if (!range) { + return undefined; + } + + + const drawUnderline = false; + const drawStrikeThrough = false; + const doNotMergeHorizontallyAlignedRects = drawUnderline || drawStrikeThrough; + //const clientRects = DEBUG_VISUALS ? range.getClientRects() : + const clientRects = getClientRectsNoOverlap(range, doNotMergeHorizontallyAlignedRects); + + return clientRects; + +} + +function rangeInfo2Location(rangeInfo) { + return { + cssSelector: rangeInfo.startContainerElementCssSelector, + partialCfi: rangeInfo.cfi, + domRange: { + start: { + cssSelector: rangeInfo.startContainerElementCssSelector, + textNodeIndex: rangeInfo.startContainerChildTextNodeIndex, + offset: rangeInfo.startOffset + }, + end: { + cssSelector: rangeInfo.endContainerElementCssSelector, + textNodeIndex: rangeInfo.endContainerChildTextNodeIndex, + offset: rangeInfo.endOffset + } + } + } +} + +function location2RangeInfo(location) { + const locations = location.locations + const domRange = locations.domRange + const start = domRange.start + const end = domRange.end + + return { + cfi: location.partialCfi, + endContainerChildTextNodeIndex: end.textNodeIndex, + endContainerElementCssSelector: end.cssSelector, + endOffset: end.offset, + startContainerChildTextNodeIndex: start.textNodeIndex, + startContainerElementCssSelector: start.cssSelector, + startOffset: start.offset + }; +} + +function rectangleForHighlightWithID(id) { + + + const highlight = highlightWithID(id); + if (!highlight) + return; + + const document = window.document; + const scrollElement = getScrollingElement(document); + const range = convertRangeInfo(document, highlight.rangeInfo); + if (!range) { + return undefined; + } + + + const drawUnderline = false; + const drawStrikeThrough = false; + const doNotMergeHorizontallyAlignedRects = drawUnderline || drawStrikeThrough; + //const clientRects = DEBUG_VISUALS ? range.getClientRects() : + const clientRects = getClientRectsNoOverlap(range, doNotMergeHorizontallyAlignedRects); + var size = { + screenWidth: window.outerWidth, + screenHeight: window.outerHeight, + left: clientRects[0].left, + width: clientRects[0].width, + top: clientRects[0].top, + height: clientRects[0].height + } + + return size; + +} + +function getSelectionRect() { + try { + var sel = window.getSelection(); + if (!sel) { + return; + } + var range = sel.getRangeAt(0); + + const clientRect = range.getBoundingClientRect(); + + var handleBounds = { + screenWidth: window.outerWidth, + screenHeight: window.outerHeight, + left: clientRect.left, + width: clientRect.width, + top: clientRect.top, + height: clientRect.height + }; + return handleBounds; + } + catch (e) { + return null; + } +}; + +function setScrollMode(flag) { + if (!flag) { + document.documentElement.classList.add(CLASS_PAGINATED); + } else { + document.documentElement.classList.remove(CLASS_PAGINATED); + } +} + +/* + if (document.addEventListener) { // IE >= 9; other browsers + document.addEventListener('contextmenu', function(e) { + //alert("You've tried to open context menu"); //here you draw your own menu + //e.preventDefault(); + //let getCssSelector = fullQualifiedSelector; + + let str = window.getSelection(); + let selectionInfo = getCurrentSelectionInfo(); + let pos = createHighlight(selectionInfo,{red:10,green:50,blue:230},true); + let ret2 = createAnnotation(pos.id); + + }, false); + } else { // IE < 9 + document.attachEvent('oncontextmenu', function() { + alert("You've tried to open context menu"); + window.event.returnValue = false; + }); + } +*/ diff --git a/r2-navigator/src/main/assets/scripts/touchHandling.js b/r2-navigator/src/main/assets/scripts/touchHandling.js new file mode 100644 index 00000000..baf2390f --- /dev/null +++ b/r2-navigator/src/main/assets/scripts/touchHandling.js @@ -0,0 +1,73 @@ +var singleTouchGesture = false; +var startX = 0; +var startY = 0; +var availWidth = window.screen.availWidth; +var availHeight = window.screen.availHeight; + + + +window.addEventListener("load", function() { // on page load + // Get screen X and Y sizes. + // Events listeners for the touches. + window.document.addEventListener("touchstart", handleTouchStart, false); + window.document.addEventListener("touchend", handleTouchEnd, false); + window.document.addEventListener("click", handleClick, true); + // When device orientation changes, screen X and Y sizes are recalculated. +}, false); + + +var handleClick = function(event) { + Android.handleClick(event.target.outerHTML) +}; + + +// When a touch is detected records its starting coordinates and if it's a singleTouchGesture. +var handleTouchStart = function(event) { + if (event.target.nodeName.toUpperCase() === 'A') { + console.log("Touched a link."); + // singleTouchGesture = false; + return; + } + console.log("Touch sent to native code."); + singleTouchGesture = event.touches.length == 1; + + var touch = event.changedTouches[0]; + + startX = touch.screenX % availWidth; + startY = touch.screenY % availHeight; + +}; + +// When a touch ends, check if any action has to be made, and contact native code. +var handleTouchEnd = function(event) { + if (!singleTouchGesture) { + return; + } + + var touch = event.changedTouches[0]; + + var relativeDistanceX = Math.abs(((touch.screenX % availWidth) - startX) / availWidth); + var relativeDistanceY = Math.abs(((touch.screenY % availHeight) - startY) / availHeight); + var touchDistance = Math.max(relativeDistanceX, relativeDistanceY); + + var scrollWidth = document.scrollWidth; + var screenWidth = availWidth; + var tapAreaWidth = availWidth * 0.2; + + if (touchDistance < 0.01) { + var position = (touch.screenX % availWidth) / availWidth; + if (position <= 0.2) { + console.log("LeftTapped"); + Android.scrollLeft(false); + } else if (position >= 0.8) { + console.log("RightTapped"); + Android.scrollRight(false); + } else { + console.log("CenterTapped"); + Android.centerTapped(); + } + event.stopPropagation(); + event.preventDefault(); + return; + } +}; diff --git a/r2-navigator/src/main/assets/scripts/utils.js b/r2-navigator/src/main/assets/scripts/utils.js new file mode 100644 index 00000000..4f89a4d9 --- /dev/null +++ b/r2-navigator/src/main/assets/scripts/utils.js @@ -0,0 +1,318 @@ +// Notify native code that the page has loaded. +window.addEventListener("load", function() { // on page load + // Notify native code that the page is loaded. + if (isScrollModeEnabled()) { + console.log("last_known_scrollY_position " + last_known_scrollY_position); + } else { + console.log("last_known_scrollX_position " + last_known_scrollX_position); + } +}, false); + +var last_known_scrollX_position = 0; +var last_known_scrollY_position = 0; +var ticking = false; + +// Position in range [0 - 1]. +var update = function(position) { + let positionString = position.toString() + Android.progressionDidChange(positionString); +}; + +function isScrollModeEnabled() { + return document.documentElement.style.getPropertyValue("--USER__scroll").toString().trim() == 'readium-scroll-on'; +} + +window.addEventListener('scroll', function(e) { + last_known_scrollY_position = window.scrollY / document.scrollingElement.scrollHeight; + last_known_scrollX_position = Math.abs(window.scrollX / document.scrollingElement.scrollWidth); + console.log("last_known_scrollX_position " + last_known_scrollX_position); + console.log("last_known_scrollY_position " + last_known_scrollY_position); + if (!ticking) { + window.requestAnimationFrame(function() { + update(isScrollModeEnabled() ? last_known_scrollY_position : last_known_scrollX_position); + ticking = false; + }); + } + ticking = true; +}); + +var uScrollWidth = function() { + return document.scrollingElement.scrollWidth +}; +var uScrollX = function() { + return window.scrollX +}; + +var scrollToPage = function(page) { + console.log("scrollToPage " + page); + + var offset = window.innerWidth * page; + + document.scrollingElement.scrollLeft = snapOffset(offset); + last_known_scrollX_position = window.scrollX / document.scrollingElement.scrollWidth; + update(last_known_scrollX_position); + + return document.scrollingElement.scrollLeft +}; + +// Scroll to the given TagId in document and snap. +var scrollToId = function(id) { + var element = document.getElementById(id); + var elementOffset = element.scrollLeft // element.getBoundingClientRect().left works for Gutenbergs books + var offset = Math.round(window.scrollX + elementOffset); + + document.scrollingElement.scrollLeft = snapOffset(offset); +}; + +// Position must be in the range [0 - 1], 0-100%. +var scrollToPosition = function(position) { + console.log("ScrollToPosition " + position); + if ((position < 0) || (position > 1)) { + console.log("InvalidPosition"); + return; + } + var offset = document.scrollingElement.scrollWidth * position; + + document.scrollingElement.scrollLeft = snapOffset(offset); + update(position); +}; + +var scrollToEnd = function() { + if (!isScrollModeEnabled()) { + console.log("scrollToEnd " + document.scrollingElement.scrollWidth); + document.scrollingElement.scrollLeft = document.scrollingElement.scrollWidth; + } else { + console.log("scrollToBottom " + document.body.scrollHeight); + document.scrollingElement.scrollTop = document.body.scrollHeight; + window.scrollTo(0, document.body.scrollHeight); + } +}; + +var scrollToStart = function() { + if (!isScrollModeEnabled()) { + console.log("scrollToStart " + 0); + document.scrollingElement.scrollLeft = 0; + } else { + console.log("scrollToTop " + 0); + document.scrollingElement.scrollTop = 0; + window.scrollTo(0, 0); + } +}; + +var scrollToPosition = function(position, dir) { + console.log("ScrollToPosition " + position); + if ((position < 0) || (position > 1)) { + console.log("InvalidPosition"); + return; + } + + if (!isScrollModeEnabled()) { + var offset = 0; + if (dir == 'rtl') { + offset = (-document.scrollingElement.scrollWidth + window.innerWidth) * (1.0 - position); + } else { + offset = document.scrollingElement.scrollWidth * position; + } + document.scrollingElement.scrollLeft = snapOffset(offset); + update(position); + } else { + var offset = Math.round(document.body.scrollHeight * position); + document.scrollingElement.scrollTop = offset; + window.scrollTo(0, offset); + update(position); + } +}; + +var scrollLeft = function() { + console.log("scrollLeft"); + + var offset = Math.round(window.scrollX - window.innerWidth); + if (offset >= 0) { + document.scrollingElement.scrollLeft = snapOffset(offset); + last_known_scrollX_position = window.scrollX / document.scrollingElement.scrollWidth; + update(last_known_scrollX_position); + return ""; + } else { + document.scrollingElement.scrollLeft = 0; + update(1.0); + return "edge"; // Need to previousDocument. + } +}; + +var scrollLeftRTL = function() { + console.log("scrollLeftRTL"); + + var scrollWidth = document.scrollingElement.scrollWidth; + var offset = Math.round(window.scrollX - window.innerWidth); + var edge = -scrollWidth + window.innerWidth; + + if (window.innerWidth == scrollWidth) { + // No scroll and default zoom + return "edge"; + } else { + // Scrolled and zoomed + if (offset > edge) { + document.scrollingElement.scrollLeft = snapOffset(offset) + return 0; + } else { + var oldOffset = window.scrollX; + document.scrollingElement.scrollLeft = edge; + var diff = Math.abs(edge - oldOffset) / window.innerWidth; + // In some case the scrollX cannot reach the position respecting to innerWidth + if (diff > 0.01) { + return 0; + } else { + return "edge"; + } + } + } +}; + +var scrollRight = function() { + console.log("scrollRight"); + var offset = Math.round(window.scrollX + window.innerWidth); + var scrollWidth = document.scrollingElement.scrollWidth; + + if (offset < scrollWidth) { + console.log("offset < scrollWidth"); + + document.scrollingElement.scrollLeft = snapOffset(offset); + var newScrollPos = window.scrollX / document.scrollingElement.scrollWidth + if ((newScrollPos - last_known_scrollX_position) > 0.001) { + last_known_scrollX_position = window.scrollX / document.scrollingElement.scrollWidth; + update(last_known_scrollX_position); + } else { + var newoffset = Math.round(window.scrollX + window.innerWidth); + document.scrollingElement.scrollLeft = snapOffset(newoffset); + last_known_scrollX_position = window.scrollX / document.scrollingElement.scrollWidth; + update(last_known_scrollX_position); + } + return ""; + } else { + console.log("else"); + document.scrollingElement.scrollLeft = scrollWidth; + last_known_scrollX_position = scrollWidth; + update(0.0); + return "edge"; // Need to nextDocument. + } +}; + +var scrollRightRTL = function() { + console.log("scrollRightRTL"); + + var scrollWidth = document.scrollingElement.scrollWidth; + var offset = Math.round(window.scrollX + window.innerWidth); + var edge = 0; + + if (window.innerWidth == scrollWidth) { + // No scroll and default zoom + return "edge"; + } else { + // Scrolled and zoomed + if (offset < edge) { + document.scrollingElement.scrollLeft = snapOffset(offset) + return 0; + } else { + var oldOffset = window.scrollX; + document.scrollingElement.scrollLeft = edge; + var diff = Math.abs(edge - oldOffset) / window.innerWidth; + // In some case the scrollX cannot reach the position respecting to innerWidth + if (diff > 0.01) { + return 0; + } else { + return "edge"; + } + } + } +}; + +// Snap the offset to the screen width (page width). +var snapOffset = function(offset) { + var value = offset + 1; + + return value - (value % window.innerWidth); +}; + +/// User Settings. + +// For setting user setting. +var setProperty = function(key, value) { + var root = document.documentElement; + + root.style.setProperty(key, value); +}; + +// For removing user setting. +var removeProperty = function(key) { + var root = document.documentElement; + + root.style.removeProperty(key); +}; + + +// TODO Work In Progress + +//Highlighting related +var setHighlight = function() { + var paragraphs = document.getElementsByClassName("highlighted"); + for (var i=0 ; i", + spanEnd = ""; + var readByTTS = spanBegin + searchText + spanEnd; + var html = currentNode.nodeValue.replace(regex, readByTTS), + wrap = document.createElement('div'), + fragm = document.createDocumentFragment(); + wrap.innerHTML = html; + while (wrap.firstChild) { + fragm.appendChild(wrap.firstChild); + } + return fragm; + })(); + + parent.insertBefore(frag, currentNode); + parent.removeChild(currentNode); + setHighlight(); + } +}; \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/fonts/OpenDyslexic-Regular.otf b/r2-navigator/src/main/assets/static/fonts/OpenDyslexic-Regular.otf new file mode 100644 index 00000000..1226d2ab Binary files /dev/null and b/r2-navigator/src/main/assets/static/fonts/OpenDyslexic-Regular.otf differ diff --git a/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-after.css b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-after.css new file mode 100644 index 00000000..020c57c7 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-after.css @@ -0,0 +1,688 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Pagination module + + A set of styles to paginate ePublications + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* Columns are responsive by default, even if column-width is set in pixels, + which means two-page spread will switch to single page depending on current font-size. + If you want more control, I’m afraid you’ll have to update colWidth/colGap dynamically, + which is how a significant amount of RS do at the moment. */ + +/* Default for smartphone portrait (small screens) */ + +:root { + /* Your columns’ width floor */ + --RS__colWidth: 45em; /* The width at which we’ll switch to 2 columns by default. PS: you can’t set it in rem */ + + /* Ideal number of columns (depending on columns’ width floor) */ + --RS__colCount: 1; + + /* Gap between columns (in pixels so that it won’t resize with font-size) */ + --RS__colGap: 0; + + /* Optimal line-length (rem will take :root font-size into account, whatever the body’s font-size) */ + --RS__maxLineLength: 40rem; + + /* Default page horizontal margins (in pixels so that it won’t resize with font-size) */ + --RS__pageGutter: 20px; /* See if colGap and pageGutter can be the same var */ +} + +/* Reset page margins for Forward compatibility */ + +@page { + margin: 0 !important; +} + +/* :root selector has same specificity as a class i.e. 0010 + We might have to change that to html / context + -> https://css-tricks.com/almanac/selectors/r/root/ */ + +:root { + + /* In case you use left position to scroll, can be removed if using transform: translateX() */ + position: relative; + + -webkit-column-width: var(--RS__colWidth); + -moz-column-width: var(--RS__colWidth); + column-width: var(--RS__colWidth); + + /* Init pagination */ + /* TODO: document columns’ logic cos it might feel weird at first */ + -webkit-column-count: var(--RS__colCount); + -moz-column-count: var(--RS__colCount); + column-count: var(--RS__colCount); + + -webkit-column-gap: var(--RS__colGap); + -moz-column-gap: var(--RS__colGap); + column-gap: var(--RS__colGap); + + /* Default is balance and we want columns to be filled entirely (100vh) */ + -moz-column-fill: auto; + column-fill: auto; + width: 100%; + height: 100vh; + max-width: 100%; + max-height: 100vh; + min-width: 100%; + min-height: 100vh; + padding: 0 !important; + margin: 0 !important; + + /* Column size will depend on this if we want to make it responsive */ + font-size: 100% !important; + + -webkit-text-size-adjust: 100%; + + /* Switch to newer box model (not inherited by authors’ styles) */ + box-sizing: border-box; + + /* Fix bug for older Chrome */ + -webkit-perspective: 1; + /* Prevents options pop-up when long tap in webkit */ + -webkit-touch-callout: none; +} + +body { + /* overflow: hidden; bugfix: contents won’t paginate in Firefox and one sample in Safari */ + width: 100%; + + /* Limit line-length but we have to reset when 2 columns and control the viewport. + By using max-width + margin auto, margins will shrink when font-size increases, + which is what would be expected in terms of typography. */ + max-width: var(--RS__maxLineLength) !important; + padding: 0 var(--RS__pageGutter) !important; + margin: 0 auto !important; + + /* We need a minimum padding on body so that descandants/ascendants in italic/script are not cut-off. + Drawback: we have to use border-box so that it doesn’t screw the box model, + which means it impacts colWidth and max-width */ + box-sizing: border-box; +} + +/* We’ll now redefine margins and columns depending on the minimum width available + The goal is having the simplest model possible and avoid targeting devices */ + +/* Smartphone landscape */ + +@media screen and (min-width: 35em) { + :root { + --RS__pageGutter: 30px; + } +} + +/* Tablet portrait */ + +@media screen and (min-width: 45em) { + :root { + --RS__pageGutter: 40px; + } +} + +/* Desktop + tablet large */ + +/* We get the previous settings, we just change the margins */ + +@media screen and (min-width: 75em) { + :root { + --RS__pageGutter: 50px; + } +} + +/* At this point (80em or so), constraining line length must be done at the web view/iframe level, or by limiting the size of :root itself */ + +/* Responsive columns */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root { + /* The size at which we want 2 columns to switch to 1 (depending on font-size) */ + --RS__colWidth: 20em; /* 20 * 16 = 320px but 20 * 28 = 560px so it will switch to 1 col @ 175% font-size (user-setting) on an iPad */ + /* We constrain to 2 columns so that we can never get 3 or 4, etc. */ + --RS__colCount: 2; + --RS__maxLineLength: 39.99rem; /* If we don’t use this, colNumber user setting won’t work in Safari… */ + } +} + +/* Readium CSS + Scroll module + + A set of styles to scroll ePublications + This module overrides pagination + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-scroll-on"] { + + /* Reset columns, auto + auto = columns can’t be created */ + -webkit-columns: auto auto !important; + -moz-columns: auto auto !important; + columns: auto auto !important; + width: auto !important; + height: auto !important; + max-width: none !important; + max-height: none !important; + /* Reset html size so that the user can scroll */ + min-width: 0 !important; + min-height: 0 !important; +} + +/* Make sure line-length is limited in all configs */ + +:root[style*="readium-scroll-on"] body { + --RS__maxLineLength: 40rem !important; + + margin-top: var(--RS__pageGutter) !important; + margin-bottom: var(--RS__pageGutter) !important; +} + +/* Scroll mode horizontal */ + +/* Vertical writing needs body height set */ + +/* Do we add a top/bottom margin for body in vertical scroll or not? */ + +/* Readium CSS + Default highlights + + A stylesheet for user highlights + + Repo: https://github.com/readium/readium-css */ + +/* User Highlights */ + +.readiumCSS-yellow-highlight { + background-color: rgba(255, 255, 0, 0.5); +} + +.readiumCSS-green-highlight { + background-color: rgba(0, 255, 0, 0.5); +} + +.readiumCSS-orange-highlight { + background-color: rgba(255, 165, 0, 0.5); +} + +.readiumCSS-pink-highlight { + background-color: rgba(255, 105, 180, 0.5); +} + +/* Media overlays */ + +.readiumCSS-mo-active-default { + color: black !important; + background-color: yellow !important; +} + +/* Readium CSS + Night mode + + A preset theme for night mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +/* [style*="--USER__appearance"] can be used to increase specificity but performance hit */ + +:root[style*="readium-night-on"] { + --RS__backgroundColor: #000000; + --RS__textColor: #FEFEFE; + + --RS__linkColor: #63caff; + --RS__visitedColor: #0099E5; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-night-on"] *:not(a) { + color: inherit !important; + background-color: transparent !important; + border-color: currentColor !important; +} + +:root[style*="readium-night-on"] svg text { + fill: currentColor !important; + stroke: none !important; +} + +:root[style*="readium-night-on"] a:link, +:root[style*="readium-night-on"] a:link * { + color: var(--RS__linkColor) !important; +} + +:root[style*="readium-night-on"] a:visited, +:root[style*="readium-night-on"] a:visited * { + color: var(--RS__visitedColor) !important; +} + +:root[style*="readium-night-on"] img[class*="gaiji"], +:root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); +} + +/* Invert all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken and invert on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%) invert(100%); + filter: brightness(80%) invert(100%); +} + +/* Readium CSS + Sepia mode + + A preset theme for sepia mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root[style*="readium-sepia-on"] { + --RS__backgroundColor: #faf4e8; + --RS__textColor: #121212; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; + + --RS__maxLineLength: 40.01rem; /* Forcing a reflow in Blink/Webkit so that blend mode can work */ +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-sepia-on"] body { + /* Should be transparent but Chrome bug https://bugs.chromium.org/p/chromium/issues/detail?id=711955&q=mix-blend-mode&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified */ + + color: inherit; + background-color: var(--RS__backgroundColor); +} + +:root[style*="readium-sepia-on"] a:link, +:root[style*="readium-sepia-on"] a:link * { + color: var(--RS__linkColor); +} + +:root[style*="readium-sepia-on"] a:visited, +:root[style*="readium-sepia-on"] a:visited * { + color: var(--RS__visitedColor); +} + +:root[style*="readium-sepia-on"] svg, +:root[style*="readium-sepia-on"] img { + /* Make sure the proper bg-color is used for the blend mode */ + background-color: transparent !important; + mix-blend-mode: multiply; +} + +/* Readium CSS + OS Accessibility Modes + + A stylesheet to deal with OS accessibility settings + + Repo: https://github.com/readium/readium-css */ + +/* Windows high contrast colors are mapped to CSS system color keywords + See http://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast */ + +@media screen and (-ms-high-contrast: active) { + :root { + color: windowText !important; + background-color: window !important; + } + + /* The following selectors are super funky but it makes sure everything is inherited, this is indeed critical for this mode */ + :root :not(#\#):not(#\#):not(#\#), + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) { + color: inherit !important; + background-color: inherit !important; + } + + .readiumCSS-mo-active-default { + color: highlightText !important; + background-color: highlight !important; + } + + /* For links, hyperlink keyword is automatically set */ + + /* Should we also set user highlights? */ +} + +@media screen and (-ms-high-contrast: white-on-black) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +/* Will be true on recent versions of iOS and MacOS if inverted setting enabled by the user */ + +@media screen and (inverted-colors) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +@media screen and (monochrome) { + /* Grayscale (Implemented in Safari, what about eInk?) */ + /* Must deal with anything color (contrast) so must be managed at the night/sepia/theme level :( */ +} + +@media screen and (prefers-reduced-motion) { + /* If reduced motion is set on MacOS, in case we have animation/transition */ +} + +/* Readium CSS + Columns number pref + + A submodule managing columns number for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Number of columns = 1 | 2 */ + +/* We still need to see if we allow users to force number of columns for all configs, currently it behaves as an "auto" setting */ + +/* apply col setting except for mobile portrait */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"], + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + -webkit-column-count: var(--USER__colCount); + -moz-column-count: var(--USER__colCount); + column-count: var(--USER__colCount); + } + + /* If one column, make sure we limit line-length */ + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"] { + --RS__maxLineLength: 40rem !important; /* This is the only way for the user setting to work in webkit… */ + --RS__colWidth: 100vw; + } + + /* If smartphone landscape, and 2 columns, col width the same as iPad landscape + desktop */ + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + --RS__colWidth: auto; /* User explicitely tells he/she wants 2 columns, we reset floor value */ + } +} + +/* Readium CSS + Page margins pref + + A submodule managing page margins for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Page Margins: the user margin is a factor of the page gutter e.g. 0.5, 0.75, 1, 1.25, 1.5, etc. */ + +:root[style*="--USER__pageMargins"] body { + padding: 0 calc(var(--RS__pageGutter) * var(--USER__pageMargins)) !important; +} + +/* Readium CSS + Custom colors pref + + A submodule managing custom colors for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__backgroundColor"] { + background-color: var(--USER__backgroundColor) !important; +} + +:root[style*="--USER__backgroundColor"] * { + background-color: transparent !important; +} + +:root[style*="--USER__textColor"] { + color: var(--USER__textColor) !important; +} + +:root[style*="--USER__textColor"] *:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) { + color: inherit !important; +} + +/* Readium CSS + Font Family pref + + A submodule managing font-family for user settings + Part of “User Overrides” class – “font override” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] { + font-family: var(--USER__fontFamily) !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] body, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] p, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] li, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] div, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dt, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dd { + font-family: inherit !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([xml\:lang]) { + font-family: inherit !important; +} + +/* Readium CSS + Font size pref + + A submodule managing font-size for user settings + Part of “User Overrides” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__fontSize"] { + font-size: var(--USER__fontSize) !important; +} + +/* Readium CSS + Line height pref + + A submodule managing line-height for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] { + line-height: var(--USER__lineHeight) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] body, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] p, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] li, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] div { + line-height: inherit; +} + +/* Readium CSS + Para spacing pref + + A submodule managing paragraphs’ top and bottom margins for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraSpacing"] p { + margin-top: var(--USER__paraSpacing) !important; + margin-bottom: var(--USER__paraSpacing) !important; +} + +/* Readium CSS + Font size normalize + + A stylesheet to normalize font-size + + Repo: https://github.com/readium/readium-css */ + +/* STYLES */ + +/* :root is used so that you can quickly add a class or attribute if you prefer e.g. `:root[data-rs-normalize]` */ + +/* We create a default so that you don’t need to explicitly set one in the DOM. + Once the “Publisher’s styles” checkbox is unchecked, the normalize is applied automatically */ + +:root[style*="readium-advanced-on"] { + --USER__typeScale: 1.2; /* This is the default type scale you’ll find in most publications */ +} + +:root[style*="readium-advanced-on"] p, +:root[style*="readium-advanced-on"] li, +:root[style*="readium-advanced-on"] div, +:root[style*="readium-advanced-on"] pre, +:root[style*="readium-advanced-on"] dd { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] h1 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.75rem !important; + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h2 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.5rem !important; + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h3 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.25rem !important; + font-size: calc(1rem * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h4, +:root[style*="readium-advanced-on"] h5, +:root[style*="readium-advanced-on"] h6 { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] small { + font-size: smaller !important; +} + +:root[style*="readium-advanced-on"] sub, +:root[style*="readium-advanced-on"] sup { + font-size: 67.5% !important; +} + +/* The following styles kick in if you define the typeScale variable in the DOM. + No need to repeat declarations which don’t make use of the variable */ + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h1 { + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h2 { + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h3 { + font-size: calc(1rem * var(--USER__typeScale)) !important; +} +/*# sourceMappingURL=ReadiumCSS-after.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-before.css b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-before.css new file mode 100644 index 00000000..28778589 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-before.css @@ -0,0 +1,575 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Base module + + A minimal stylesheet for all ebooks + + Repo: https://github.com/readium/readium-css */ + +@namespace url("http://www.w3.org/1999/xhtml"); + +@namespace epub url("http://www.idpf.org/2007/ops"); + +@namespace m url("http://www.w3.org/1998/Math/MathML/"); + +@namespace svg url("http://www.w3.org/2000/svg"); + +/* Define viewport, HTML5-style */ + +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; + zoom: 1; +} + +:root { + /* Default font-stacks */ + --RS__oldStyleTf: "Iowan Old Style", "Sitka Text", Palatino, "Book Antiqua", serif; + --RS__modernTf: Athelas, Constantia, Georgia, serif; + --RS__sansTf: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --RS__humanistTf: Seravek, Calibri, Roboto, Arial, sans-serif; + --RS__monospaceTf: "Andale Mono", Consolas, monospace; + + /* Config */ + --RS__baseFontFamily: var(--RS__oldStyleTf); + + /* For square-ish fonts (CJK, Indic, etc.), we must apply some compensation in dynamic leading. Default is 1 i.e. no compensation */ + --RS__lineHeightCompensation: 1; + + /* Dynamic leading based on typeface metrics + font-size setting */ + --RS__baseLineHeight: calc((1em + (2ex - 1ch) - ((1rem - 16px) * 0.1667)) * var(--RS__lineHeightCompensation)); +} + +/* Set default font for the html doc, so that it can be overridden by the authors’s stylesheet */ + +html { + font-family: var(--RS__baseFontFamily); + /* Fallback line-height */ + line-height: 1.6; /* Fits a little bit better for all languages than 1.5 */ + line-height: var(--RS__baseLineHeight); + text-rendering: optimizeLegibility; +} + +/* 1.5 being too loose with larger font-sizes, we reset headings to normal (which value is 1.125–1.375 for our font-stacks) */ + +h1, h2, h3 { + line-height: normal; +} + +:lang(ja), +:lang(zh), +:lang(ko) { + word-wrap: break-word; + -webkit-line-break: strict; + -epub-line-break: strict; + line-break: strict; +} + +/* Set default font for Math */ + +math { + font-family: "Latin Modern Math", "STIX Two Math", "XITS Math", "STIX Math", "Libertinus Math", "TeX Gyre Termes Math", "TeX Gyre Bonum Math", "TeX Gyre Schola", "DejaVu Math TeX Gyre", "TeX Gyre Pagella Math", "Asana Math", "Cambria Math", "Lucida Bright Math", "Minion Math", STIXGeneral, STIXSizeOneSym, Symbol, "Times New Roman", serif; +} + +/* Language Overrides + That will only work if either html or body have a (xml:)lang attribute, not for inline overrides */ + +:lang(am) { + --RS__baseFontFamily: Kefa, Nyala, Roboto, Noto, "Noto Sans Ethiopic", serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ar) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(bn) { + --RS__baseFontFamily: "Kohinoor Bangla", "Bangla Sangam MN", Vrinda, Roboto, Noto, "Noto Sans Bengali", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(bo) { + --RS__baseFontFamily: Kailasa, "Microsoft Himalaya", Roboto, Noto, "Noto Sans Tibetan", sans-serif; +} + +:lang(chr) { + --RS__baseFontFamily: "Plantagenet Cherokee", Roboto, Noto, "Noto Sans Cherokee"; + --RS__lineHeightCompensation: 1.167; +} + +:lang(fa) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(gu) { + --RS__baseFontFamily: "Gujarati Sangam MN", "Nirmala UI", Shruti, Roboto, Noto, "Noto Sans Gujarati", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(he) { + --RS__baseFontFamily: "New Peninim MT", "Arial Hebrew", Gisha, "Times New Roman", Roboto, Noto, "Noto Sans Hebrew" sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(hi) { + --RS__baseFontFamily: "Kohinoor Devanagari", "Devanagari Sangam MN", Kokila, "Nirmala UI", Roboto, Noto, "Noto Sans Devanagari", sans-serif; + + --RS__lineHeightCompensation: 1.1; +} + +:lang(hy) { + --RS__baseFontFamily: Mshtakan, Sylfaen, Roboto, Noto, "Noto Serif Armenian", serif; +} + +:lang(iu) { + --RS__baseFontFamily: "Euphemia UCAS", Euphemia, Roboto, Noto, "Noto Sans Canadian Aboriginal", sans-serif; +} + +:lang(ja) { + --RS__baseFontFamily: "游ゴシック体", YuGothic, "ヒラギノ丸ゴ", "Hiragino Sans", "Yu Gothic UI", "Meiryo UI", "MS Gothic", Roboto, Noto, "Noto Sans CJK JP", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; + + /* Extra variables for Japanese font-stacks as we may want to reuse them for user settings + default */ + --RS__serif-ja: "MS P明朝", "MS PMincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS 明朝", "MS Mincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja: "MS Pゴシック", "MS PGothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS ゴシック", "MS Gothic", "Hiragino Sans", sans-serif; + --RS__serif-ja-v: "MS 明朝", "MS Mincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS P明朝", "MS PMincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja-v: "MS ゴシック", "MS Gothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS Pゴシック", "MS PGothic", "Hiragino Sans", sans-serif; +} + +:lang(km) { + --RS__baseFontFamily: "Khmer Sangam MN", "Leelawadee UI", "Khmer UI", Roboto, Noto, "Noto Sans Khmer", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(kn) { + --RS__baseFontFamily: "Kannada Sangam MN", "Nirmala UI", Tunga, Roboto, Noto, "Noto Sans Kannada", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(ko) { + --RS__baseFontFamily: "Nanum Gothic", "Apple SD Gothic Neo", "Malgun Gothic", Roboto, Noto, "Noto Sans CJK KR", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(lo) { + --RS__baseFontFamily: "Lao Sangam MN", "Leelawadee UI", "Lao UI", Roboto, Noto, "Noto Sans Lao", sans-serif; +} + +:lang(ml) { + --RS__baseFontFamily: "Malayalam Sangam MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Malayalam", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(or) { + --RS__baseFontFamily: "Oriya Sangam MN", "Nirmala UI", Kalinga, Roboto, Noto, "Noto Sans Oriya", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(pa) { + --RS__baseFontFamily: "Gurmukhi MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Gurmukhi", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(si) { + --RS__baseFontFamily: "Sinhala Sangam MN", "Nirmala UI", "Iskoola Pota", Roboto, Noto, "Noto Sans Sinhala", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ta) { + --RS__baseFontFamily: "Tamil Sangam MN", "Nirmala UI", Latha, Roboto, Noto, "Noto Sans Tamil", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(te) { + --RS__baseFontFamily: "Kohinoor Telugu", "Telugu Sangam MN", "Nirmala UI", Gautami, Roboto, Noto, "Noto Sans Telugu", sans-serif; +} + +:lang(th) { + --RS__baseFontFamily: "Thonburi", "Leelawadee UI", "Cordia New", Roboto, Noto, "Noto Sans Thai", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +/* The following will also work for zh-Hans */ + +:lang(zh) { + --RS__baseFontFamily: "方体", "PingFang SC", "黑体", "Heiti SC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK SC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-Hant), +:lang(zh-TW) { + --RS__baseFontFamily: "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-HK) { + --RS__baseFontFamily: "方體", "PingFang HK", "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +/* Readium CSS + Day/Default mode + + A preset theme for day mode, which is the default + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__backgroundColor: #FFFFFF; + --RS__textColor: #121212; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +:root { + color: var(--RS__textColor) !important; + background-color: var(--RS__backgroundColor) !important; +} + +/* Note: Though `::selection` was present in drafts of CSS Selectors Level 3, it was removed during the Candidate Recommendation phase because its behavior was under-specified (especially with nested elements) and interoperability wasn’t achieved. Source: https://developer.mozilla.org/en-US/docs/Web/CSS/::selection */ + +::-moz-selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +::selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +/* @import "modules/ReadiumCSS-fonts.css"; */ + +/* Readium CSS + HTML5 SR Patch stylesheet + + A set of style to adjust HTML5 Suggested Rendering to paginated content + + Repo: https://github.com/readium/readium-css */ + +/* Fragmentation */ + +body { + widows: 2; + orphans: 2; +} + +figcaption, th, td { + widows: 1; + orphans: 1; +} + +h2, h3, h4, h5, h6, dt, +hr, caption { + -webkit-column-break-after: avoid; + page-break-after: avoid; + break-after: avoid; +} + +h1, h2, h3, h4, h5, h6, dt, +figure, tr { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Hyphenation */ + +body { + -webkit-hyphenate-character: "\002D"; + -moz-hyphenate-character: "\002D"; + -ms-hyphenate-character: "\002D"; + hyphenate-character: "\002D"; + -webkit-hyphenate-limit-lines: 3; + -ms-hyphenate-limit-lines: 3; + hyphenate-limit-lines: 3; +} + +h1, h2, h3, h4, h5, h6, dt, +figcaption, pre, caption, address, +center, code, var { + -ms-hyphens: none; + -moz-hyphens: none; + -webkit-hyphens: none; + -epub-hyphens: none; + hyphens: none; +} + +/* OTF */ + +body { + font-variant-numeric: oldstyle-nums proportional-nums; +} + +:lang(ja) body, +:lang(zh) body, +:lang(ko) body { + font-variant-numeric: lining-nums proportional-nums; +} + +h1, h2, h3, h4, h5, h6, dt { + font-variant-numeric: lining-nums proportional-nums; +} + +table { + font-variant-numeric: lining-nums tabular-nums; +} + +code, var { + font-variant-ligatures: none; + font-variant-numeric: lining-nums tabular-nums slashed-zero; +} + +rt { + font-variant-east-asian: ruby; +} + +:lang(ar) { + font-variant-ligatures: common-ligatures; +} + +:lang(ko) { + font-kerning: normal; +} + +/* Night mode */ + +hr { + color: inherit; + border-color: currentColor; +} + +table, th, td { + border-color: currentColor; +} + +/* Horizontal Spacing */ + +figure, blockquote { + margin: 1em 5%; +} + +/* + +:lang(ja) figure, :lang(ja) blockquote, +:lang(zh-Hant) figure, :lang(zh-Hant) blockquote, +:lang(zh-TW) figure, :lang(zh-TW) blockquote, +:lang(mn) figure, :lang(mn) blockquote { + margin: 5% 1em; +} + +*/ + +ul, ol { + padding-left: 5%; +} + +/* + +:lang(ja) ul, :lang(ja) ol, +:lang(zh-Hant) ul, :lang(zh-Hant) ol, +:lang(zh-TW) ul, :lang(zh-TW) ol, +:lang(mn) ul, :lang(mn) ol { + padding-top: 5%; +} + +*/ + +dd { + margin-left: 5%; +} + +/* + +:lang(ja) dd, +:lang(zh-Hant) dd, +:lang(zh-TW) dd, +:lang(mn) dd { + margin-top: 5%; +} + +*/ + +pre { + white-space: pre-wrap; + -ms-tab-size: 2; + -moz-tab-size: 2; + -webkit-tab-size: 2; + tab-size: 2; +} + +/* Normalization */ + +abbr[title], acronym[title] { + text-decoration: dotted underline; +} + +nobr wbr { + white-space: normal; +} + +/* Make ruby text and parentheses non-selectable (TBC) */ + +ruby > rt, ruby > rp { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Internationalization */ + +*:lang(ja), +*:lang(zh), +*:lang(ko), +:lang(ja) cite, :lang(ja) dfn, :lang(ja) em, :lang(ja) i, +:lang(zh) cite, :lang(zh) dfn, :lang(zh) em, :lang(zh) i, +:lang(ko) cite, :lang(ko) dfn, :lang(ko) em, :lang(ko) i { + font-style: normal; +} + +:lang(ja) a, +:lang(zh) a, +:lang(ko) a { + text-decoration: none; +} + +/* Readium CSS + Safeguards module + + A set of styles to prevent common issues in pagination + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* We’ll be using an "RS__" prefix so that we can prevent collisions with authors’ CSS */ + +:root { + /* max-width for media, you can override that via JS if not compiled to static */ + --RS__maxMediaWidth: 100%; + + /* max-height for media, you can override that via JS if not compiled to static + Please consider figures might have a figcaption, which is why 95vh in the first place */ + --RS__maxMediaHeight: 95vh; + + /* value for medias’ box-sizing */ + --RS__boxSizingMedia: border-box; + + /* value for table’s box-sizing */ + --RS__boxSizingTable: border-box; +} + +/* Sanitize line-heights in webkit e.g. raised cap without a declared line-height + See effect by checking this demo in Safari: https://codepen.io/JayPanoz/pen/gRmzrE + Note: glyphs has to be reset to inline for CJK */ + +html { + -webkit-line-box-contain: block glyphs replaced; +} + +:lang(ja) { + -webkit-line-box-contain: block inline replaced; +} + +/* Wrap long strings if larger than line-length */ + +a, h1, h2, h3, h4, h5, h6 { + word-wrap: break-word; +} + +div { + max-width: var(--RS__maxMediaWidth); +} + +/* Size medias */ + +/* You can override CSS variables by re-defining it for all elements or a specific one */ + +img, svg, audio, video { + + /* Object-fit allows us to keep the correct aspect-ratio */ + object-fit: contain; + + width: auto; + height: auto; + + /* Some files don’t have max-width */ + max-width: var(--RS__maxMediaWidth); + + /* We’re setting a max-height, especially for covers */ + max-height: var(--RS__maxMediaHeight) !important; + /* We probably don’t need to use modern box-sizing as auto behaves like it */ + box-sizing: var(--RS__boxSizingMedia); + + /* For page-break, we must use those 3 + We can’t use a variable there, webkit seems to no support them for those properties */ + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Try preventing border being cut-off, webkit + blink have content-box by default */ + +table { + max-width: var(--RS__maxMediaWidth); + box-sizing: var(--RS__boxSizingTable); +} +/*# sourceMappingURL=ReadiumCSS-before.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-default.css b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-default.css new file mode 100644 index 00000000..d7877db9 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-horizontal/ReadiumCSS-default.css @@ -0,0 +1,187 @@ +/* Readium CSS + Default module for CJK horizontal writing + + A stylesheet for unstyled ebooks based on HTML5 Suggested Rendering + Note: works in combination with Base module + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + /* Extra variables for Japanese font-stacks: + • --RS__serif-ja; + • --RS__sans-serif-ja. + + They can be used instead of --RS__baseFontFamily and --RS__compFontFamily */ + + --RS__compFontFamily: var(--RS__baseFontFamily); + --RS__codeFontFamily: var(--RS__monospaceTf); + + --RS__typeScale: 1.125; /* 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618 */ + --RS__baseFontSize: 87.5%; + + --RS__flowSpacing: 1.5rem; + --RS__paraSpacing: 0; + --RS__paraIndent: 1em; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + --RS__primaryColor: ; + --RS__secondaryColor: ; +} + +/* STYLES */ + +/* Typo */ + +:root { + quotes: "\201c" "\201d" "\2018" "\2019"; +} + +body { + font-size: var(--RS__baseFontSize); + text-align: justify; + text-justify: inter-character; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--RS__baseFontFamily); + text-align: left; + text-align: start; +} + +/* Flow content */ + +blockquote, figure, p, pre, +aside, footer, form, hr { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +p { + margin-top: var(--RS__paraSpacing); + margin-bottom: var(--RS__paraSpacing); + text-indent: var(--RS__paraIndent); +} + +h1 + p, h2 + p, h3 + p, h4 + p, h5 + p, h6 + p, +hr + p { + text-indent: 0; +} + +pre { + font-family: var(--RS__codeFontFamily); +} + +/* Phrasing content */ + +code, kbd, samp, tt { + font-family: var(--RS__codeFontFamily); +} + +sub, sup { + position: relative; + font-size: 67.5%; + line-height: 1; +} + +sub { + bottom: -0.2ex; +} + +sup { + bottom: 0; +} + +em { + -webkit-text-emphasis: dot; + -epub-text-emphasis: dot; + text-emphasis: dot; +} + +:link { + color: var(--RS__linkColor); +} + +:visited { + color: var(--RS__visitedColor); +} + +/* Headings */ + +h1 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: calc(var(--RS__flowSpacing) * 2); + /* The following is base font size * typescale power of 3 */ + font-size: calc(((1em * var(--RS__typeScale)) * var(--RS__typeScale)) * var(--RS__typeScale)); + text-align: center; +} + +h2 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: var(--RS__flowSpacing); + /* The following is base font size * typescale power of 2 */ + font-size: calc((1em * var(--RS__typeScale)) * var(--RS__typeScale)); + text-align: center; +} + +h3 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: calc(1em * var(--RS__typeScale)); + text-align: center; +} + +h4 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-family: var(--RS__compFontFamily); + font-size: 1em; +} + +h5 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-family: var(--RS__compFontFamily); + font-size: smaller; +} + +h6 { + margin-top: var(--RS__flowSpacing); + margin-bottom: 0; + font-family: var(--RS__compFontFamily); + font-size: smaller; + font-weight: normal; +} + +/* Lists */ + +dl, ol, ul { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +/* Table */ + +table { + margin: var(--RS__flowSpacing) 0; + border: 1px solid currentColor; + border-collapse: collapse; + empty-cells: show; +} + +thead, tbody, tfoot, table > tr { + vertical-align: top; +} + +th { + text-align: left; +} + +th, td { + padding: 4px; + border: 1px solid currentColor; +} +/*# sourceMappingURL=ReadiumCSS-default.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-after.css b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-after.css new file mode 100644 index 00000000..cb34fa28 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-after.css @@ -0,0 +1,654 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Pagination module for vertical writing + + A set of styles to paginate ePublications in “writing-mode: vertical-*” + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* We must simplify the pagination model for vertical writing, and can’t fake spreads. */ + +/* Default for smartphone portrait (small screens) */ + +:root { + /* The column will be the height of the web view/iframe */ + --RS__colWidth: 100vh; + + /* Since columns are laid out on the y-axis in vertical-*, we can only use 1 */ + --RS__colCount: 1; + + /* Gap between columns (in pixels so that it won’t resize with font-size) */ + --RS__colGap: 0; + + /* Optimal line-length (rem will take :root font-size into account, whatever the body’s font-size) */ + --RS__maxLineLength: 40rem; + + /* Default page vertical margins (in pixels so that it won’t resize with font-size) */ + --RS__pageGutter: 20px; /* See if colGap and pageGutter can be the same var */ +} + +/* Reset page margins for Forward compatibility */ + +@page { + margin: 0 !important; +} + +/* :root selector has same specificity as a class i.e. 0010 + We might have to change that to html / context + -> https://css-tricks.com/almanac/selectors/r/root/ */ + +:root { + + /* In case you use left position to scroll, can be removed if using transform: translateX() */ + position: relative; + + -webkit-column-width: var(--RS__colWidth); + -moz-column-width: var(--RS__colWidth); + column-width: var(--RS__colWidth); + + /* Init pagination */ + /* TODO: document columns’ logic cos it might feel weird at first */ + -webkit-column-count: var(--RS__colCount); + -moz-column-count: var(--RS__colCount); + column-count: var(--RS__colCount); + + -webkit-column-gap: var(--RS__colGap); + -moz-column-gap: var(--RS__colGap); + column-gap: var(--RS__colGap); + + /* Default is balance and we want columns to be filled entirely (100vh) */ + -moz-column-fill: auto; + column-fill: auto; + width: 100%; + height: 100vh; + max-width: 100%; + max-height: 100vh; + min-width: 100%; + min-height: 100vh; + padding: 0 var(--RS__pageGutter) !important; + margin: 0 !important; + + /* Column size will depend on this if we want to make it responsive */ + font-size: 100% !important; + + -webkit-text-size-adjust: 100%; + + /* Switch to newer box model (not inherited by authors’ styles) */ + box-sizing: border-box; + + hanging-punctuation: last allow-end; + + /* Fix bug for older Chrome */ + -webkit-perspective: 1; + /* Prevents options pop-up when long tap in webkit */ + -webkit-touch-callout: none; + + /* The reason why we don’t force -webkit-column-axis is that it switches the column-box model to a paged overflow model. + In other words, columns become useless, the sizing of the :root itself will be used for pagination */ + + /* Ensure the correct writing-mode is used */ + -ms-writing-mode: tb-rl; + -webkit-writing-mode: vertical-rl; + writing-mode: vertical-rl; +} + +:root:lang(mn-Mong) { + /* Ensure the correct writing-mode is used for mongolian if vertical */ + -ms-writing-mode: tb; + -webkit-writing-mode: vertical-lr; + writing-mode: vertical-lr; +} + +body { + /* overflow: hidden; bugfix: contents won’t paginate in Firefox and one sample in Safari */ + width: 100%; + + /* Limit line-length but we have to reset when 2 columns and control the viewport. + By using max-width + margin auto, margins will shrink when font-size increases, + which is what would be expected in terms of typography. */ + max-height: var(--RS__maxLineLength) !important; + padding: var(--RS__pageGutter) 0 !important; + margin: auto 0 !important; + + /* We need a minimum padding on body so that descandants/ascendants in italic/script are not cut-off. + Drawback: we have to use border-box so that it doesn’t screw the box model, + which means it impacts colWidth and max-width */ + box-sizing: border-box; +} + +/* We’ll now redefine margins and columns depending on the minimum width available + The goal is having the simplest model possible and avoid targeting devices */ + +/* Smartphone landscape */ + +@media screen and (min-width: 35em) { + :root { + --RS__pageGutter: 30px; + } +} + +/* Tablet portrait */ + +@media screen and (min-width: 45em) { + :root { + --RS__pageGutter: 40px; + } +} + +/* Desktop small + phablet + tablet landscape */ + +@media screen and (min-width: 60em) { + :root { + --RS__pageGutter: 50px; + } +} + +/* Desktop + tablet large */ + +/* We get the previous settings, we just change the margins */ + +@media screen and (min-width: 75em) { + :root { + --RS__pageGutter: 60px; + } +} + +/* At this point (80em or so), constraining line length must be done at the web view/iframe level, or by limiting the size of :root itself */ + +/* Readium CSS + Scroll module for vertical-writing + + A set of styles to scroll ePublications in “writing-mode: vertical-*” + This module overrides pagination + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-scroll-on"] { + + /* Reset columns, auto + auto = columns can’t be created */ + /* There is a weird gap in Safari/Webkit, as if overflow paged-x… */ + -webkit-columns: auto auto !important; + -moz-columns: auto auto !important; + columns: auto auto !important; + width: auto !important; + max-width: none !important; + max-height: 100vh !important; + /* Reset html size so that the user can scroll */ + min-width: 0 !important; +} + +/* Make sure line-length is limited in all configs */ + +:root[style*="readium-scroll-on"] body { + --RS__maxLineLength: 40.01rem !important; /* Fixes fragmentation update issues in Webkit i.e. value must be slightly different than the one for pagination */ +} + +/* Scroll mode horizontal */ + +/* Do we add a top/bottom margin for body in vertical scroll or not? */ + +/* Readium CSS + Default highlights + + A stylesheet for user highlights + + Repo: https://github.com/readium/readium-css */ + +/* User Highlights */ + +.readiumCSS-yellow-highlight { + background-color: rgba(255, 255, 0, 0.5); +} + +.readiumCSS-green-highlight { + background-color: rgba(0, 255, 0, 0.5); +} + +.readiumCSS-orange-highlight { + background-color: rgba(255, 165, 0, 0.5); +} + +.readiumCSS-pink-highlight { + background-color: rgba(255, 105, 180, 0.5); +} + +/* Media overlays */ + +.readiumCSS-mo-active-default { + color: black !important; + background-color: yellow !important; +} + +/* Readium CSS + Night mode + + A preset theme for night mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +/* [style*="--USER__appearance"] can be used to increase specificity but performance hit */ + +:root[style*="readium-night-on"] { + --RS__backgroundColor: #000000; + --RS__textColor: #FEFEFE; + + --RS__linkColor: #63caff; + --RS__visitedColor: #0099E5; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-night-on"] *:not(a) { + color: inherit !important; + background-color: transparent !important; + border-color: currentColor !important; +} + +:root[style*="readium-night-on"] svg text { + fill: currentColor !important; + stroke: none !important; +} + +:root[style*="readium-night-on"] a:link, +:root[style*="readium-night-on"] a:link * { + color: var(--RS__linkColor) !important; +} + +:root[style*="readium-night-on"] a:visited, +:root[style*="readium-night-on"] a:visited * { + color: var(--RS__visitedColor) !important; +} + +:root[style*="readium-night-on"] img[class*="gaiji"], +:root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); +} + +/* Invert all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken and invert on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%) invert(100%); + filter: brightness(80%) invert(100%); +} + +/* Readium CSS + Sepia mode + + A preset theme for sepia mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root[style*="readium-sepia-on"] { + --RS__backgroundColor: #faf4e8; + --RS__textColor: #121212; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; + + --RS__maxLineLength: 40.01rem; /* Forcing a reflow in Blink/Webkit so that blend mode can work */ +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-sepia-on"] body { + /* Should be transparent but Chrome bug https://bugs.chromium.org/p/chromium/issues/detail?id=711955&q=mix-blend-mode&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified */ + + color: inherit; + background-color: var(--RS__backgroundColor); +} + +:root[style*="readium-sepia-on"] a:link, +:root[style*="readium-sepia-on"] a:link * { + color: var(--RS__linkColor); +} + +:root[style*="readium-sepia-on"] a:visited, +:root[style*="readium-sepia-on"] a:visited * { + color: var(--RS__visitedColor); +} + +:root[style*="readium-sepia-on"] svg, +:root[style*="readium-sepia-on"] img { + /* Make sure the proper bg-color is used for the blend mode */ + background-color: transparent !important; + mix-blend-mode: multiply; +} + +/* Readium CSS + OS Accessibility Modes + + A stylesheet to deal with OS accessibility settings + + Repo: https://github.com/readium/readium-css */ + +/* Windows high contrast colors are mapped to CSS system color keywords + See http://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast */ + +@media screen and (-ms-high-contrast: active) { + :root { + color: windowText !important; + background-color: window !important; + } + + /* The following selectors are super funky but it makes sure everything is inherited, this is indeed critical for this mode */ + :root :not(#\#):not(#\#):not(#\#), + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) { + color: inherit !important; + background-color: inherit !important; + } + + .readiumCSS-mo-active-default { + color: highlightText !important; + background-color: highlight !important; + } + + /* For links, hyperlink keyword is automatically set */ + + /* Should we also set user highlights? */ +} + +@media screen and (-ms-high-contrast: white-on-black) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +/* Will be true on recent versions of iOS and MacOS if inverted setting enabled by the user */ + +@media screen and (inverted-colors) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +@media screen and (monochrome) { + /* Grayscale (Implemented in Safari, what about eInk?) */ + /* Must deal with anything color (contrast) so must be managed at the night/sepia/theme level :( */ +} + +@media screen and (prefers-reduced-motion) { + /* If reduced motion is set on MacOS, in case we have animation/transition */ +} + +/* Readium CSS + Page margins pref + + A submodule managing page margins for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Page Margins: the user margin is a factor of the page gutter e.g. 0.5, 0.75, 1, 1.25, 1.5, etc. */ + +:root[style*="--USER__pageMargins"] body { + padding: calc(var(--RS__pageGutter) * var(--USER__pageMargins)) 0 !important; +} + +/* Readium CSS + Custom colors pref + + A submodule managing custom colors for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__backgroundColor"] { + background-color: var(--USER__backgroundColor) !important; +} + +:root[style*="--USER__backgroundColor"] * { + background-color: transparent !important; +} + +:root[style*="--USER__textColor"] { + color: var(--USER__textColor) !important; +} + +:root[style*="--USER__textColor"] *:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) { + color: inherit !important; +} + +/* Readium CSS + Font Family pref + + A submodule managing font-family for user settings + Part of “User Overrides” class – “font override” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] { + font-family: var(--USER__fontFamily) !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] body, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] p, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] li, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] div, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dt, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dd { + font-family: inherit !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([xml\:lang]) { + font-family: inherit !important; +} + +/* Readium CSS + Font size pref + + A submodule managing font-size for user settings + Part of “User Overrides” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__fontSize"] { + font-size: var(--USER__fontSize) !important; +} + +/* Readium CSS + Line height pref + + A submodule managing line-height for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] { + line-height: var(--USER__lineHeight) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] body, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] p, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] li, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] div { + line-height: inherit; +} + +/* Readium CSS + Para spacing pref + + A submodule managing paragraphs’ top and bottom margins for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraSpacing"] p { + margin-right: var(--USER__paraSpacing) !important; + margin-left: var(--USER__paraSpacing) !important; +} + +/* Readium CSS + Font size normalize + + A stylesheet to normalize font-size + + Repo: https://github.com/readium/readium-css */ + +/* STYLES */ + +/* :root is used so that you can quickly add a class or attribute if you prefer e.g. `:root[data-rs-normalize]` */ + +/* We create a default so that you don’t need to explicitly set one in the DOM. + Once the “Publisher’s styles” checkbox is unchecked, the normalize is applied automatically */ + +:root[style*="readium-advanced-on"] { + --USER__typeScale: 1.2; /* This is the default type scale you’ll find in most publications */ +} + +:root[style*="readium-advanced-on"] p, +:root[style*="readium-advanced-on"] li, +:root[style*="readium-advanced-on"] div, +:root[style*="readium-advanced-on"] pre, +:root[style*="readium-advanced-on"] dd { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] h1 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.75rem !important; + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h2 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.5rem !important; + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h3 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.25rem !important; + font-size: calc(1rem * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h4, +:root[style*="readium-advanced-on"] h5, +:root[style*="readium-advanced-on"] h6 { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] small { + font-size: smaller !important; +} + +:root[style*="readium-advanced-on"] sub, +:root[style*="readium-advanced-on"] sup { + font-size: 67.5% !important; +} + +/* The following styles kick in if you define the typeScale variable in the DOM. + No need to repeat declarations which don’t make use of the variable */ + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h1 { + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h2 { + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h3 { + font-size: calc(1rem * var(--USER__typeScale)) !important; +} +/*# sourceMappingURL=ReadiumCSS-after.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-before.css b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-before.css new file mode 100644 index 00000000..75dc4d5c --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-before.css @@ -0,0 +1,574 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Base module + + A minimal stylesheet for all ebooks + + Repo: https://github.com/readium/readium-css */ + +@namespace url("http://www.w3.org/1999/xhtml"); + +@namespace epub url("http://www.idpf.org/2007/ops"); + +@namespace m url("http://www.w3.org/1998/Math/MathML/"); + +@namespace svg url("http://www.w3.org/2000/svg"); + +/* Define viewport, HTML5-style */ + +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; + zoom: 1; +} + +:root { + /* Default font-stacks */ + --RS__oldStyleTf: "Iowan Old Style", "Sitka Text", Palatino, "Book Antiqua", serif; + --RS__modernTf: Athelas, Constantia, Georgia, serif; + --RS__sansTf: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --RS__humanistTf: Seravek, Calibri, Roboto, Arial, sans-serif; + --RS__monospaceTf: "Andale Mono", Consolas, monospace; + + /* Config */ + --RS__baseFontFamily: var(--RS__oldStyleTf); + + /* For square-ish fonts (CJK, Indic, etc.), we must apply some compensation in dynamic leading. Default is 1 i.e. no compensation */ + --RS__lineHeightCompensation: 1; + + /* Dynamic leading based on typeface metrics + font-size setting */ + --RS__baseLineHeight: calc((1em + (2ex - 1ch) - ((1rem - 16px) * 0.1667)) * var(--RS__lineHeightCompensation)); +} + +/* Set default font for the html doc, so that it can be overridden by the authors’s stylesheet */ + +html { + font-family: var(--RS__baseFontFamily); + /* Fallback line-height */ + line-height: 1.6; /* Fits a little bit better for all languages than 1.5 */ + line-height: var(--RS__baseLineHeight); + text-rendering: optimizeLegibility; +} + +/* 1.5 being too loose with larger font-sizes, we reset headings to normal (which value is 1.125–1.375 for our font-stacks) */ + +h1, h2, h3 { + line-height: normal; +} + +:lang(ja), +:lang(zh), +:lang(ko) { + word-wrap: break-word; + -webkit-line-break: strict; + -epub-line-break: strict; + line-break: strict; +} + +/* Set default font for Math */ + +math { + font-family: "Latin Modern Math", "STIX Two Math", "XITS Math", "STIX Math", "Libertinus Math", "TeX Gyre Termes Math", "TeX Gyre Bonum Math", "TeX Gyre Schola", "DejaVu Math TeX Gyre", "TeX Gyre Pagella Math", "Asana Math", "Cambria Math", "Lucida Bright Math", "Minion Math", STIXGeneral, STIXSizeOneSym, Symbol, "Times New Roman", serif; +} + +/* Language Overrides + That will only work if either html or body have a (xml:)lang attribute, not for inline overrides */ + +:lang(am) { + --RS__baseFontFamily: Kefa, Nyala, Roboto, Noto, "Noto Sans Ethiopic", serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ar) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(bn) { + --RS__baseFontFamily: "Kohinoor Bangla", "Bangla Sangam MN", Vrinda, Roboto, Noto, "Noto Sans Bengali", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(bo) { + --RS__baseFontFamily: Kailasa, "Microsoft Himalaya", Roboto, Noto, "Noto Sans Tibetan", sans-serif; +} + +:lang(chr) { + --RS__baseFontFamily: "Plantagenet Cherokee", Roboto, Noto, "Noto Sans Cherokee"; + --RS__lineHeightCompensation: 1.167; +} + +:lang(fa) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(gu) { + --RS__baseFontFamily: "Gujarati Sangam MN", "Nirmala UI", Shruti, Roboto, Noto, "Noto Sans Gujarati", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(he) { + --RS__baseFontFamily: "New Peninim MT", "Arial Hebrew", Gisha, "Times New Roman", Roboto, Noto, "Noto Sans Hebrew" sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(hi) { + --RS__baseFontFamily: "Kohinoor Devanagari", "Devanagari Sangam MN", Kokila, "Nirmala UI", Roboto, Noto, "Noto Sans Devanagari", sans-serif; + + --RS__lineHeightCompensation: 1.1; +} + +:lang(hy) { + --RS__baseFontFamily: Mshtakan, Sylfaen, Roboto, Noto, "Noto Serif Armenian", serif; +} + +:lang(iu) { + --RS__baseFontFamily: "Euphemia UCAS", Euphemia, Roboto, Noto, "Noto Sans Canadian Aboriginal", sans-serif; +} + +:lang(ja) { + --RS__baseFontFamily: "游ゴシック体", YuGothic, "ヒラギノ丸ゴ", "Hiragino Sans", "Yu Gothic UI", "Meiryo UI", "MS Gothic", Roboto, Noto, "Noto Sans CJK JP", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; + + /* Extra variables for Japanese font-stacks as we may want to reuse them for user settings + default */ + --RS__serif-ja: "MS P明朝", "MS PMincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS 明朝", "MS Mincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja: "MS Pゴシック", "MS PGothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS ゴシック", "MS Gothic", "Hiragino Sans", sans-serif; + --RS__serif-ja-v: "MS 明朝", "MS Mincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS P明朝", "MS PMincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja-v: "MS ゴシック", "MS Gothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS Pゴシック", "MS PGothic", "Hiragino Sans", sans-serif; +} + +:lang(km) { + --RS__baseFontFamily: "Khmer Sangam MN", "Leelawadee UI", "Khmer UI", Roboto, Noto, "Noto Sans Khmer", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(kn) { + --RS__baseFontFamily: "Kannada Sangam MN", "Nirmala UI", Tunga, Roboto, Noto, "Noto Sans Kannada", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(ko) { + --RS__baseFontFamily: "Nanum Gothic", "Apple SD Gothic Neo", "Malgun Gothic", Roboto, Noto, "Noto Sans CJK KR", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(lo) { + --RS__baseFontFamily: "Lao Sangam MN", "Leelawadee UI", "Lao UI", Roboto, Noto, "Noto Sans Lao", sans-serif; +} + +:lang(ml) { + --RS__baseFontFamily: "Malayalam Sangam MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Malayalam", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(or) { + --RS__baseFontFamily: "Oriya Sangam MN", "Nirmala UI", Kalinga, Roboto, Noto, "Noto Sans Oriya", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(pa) { + --RS__baseFontFamily: "Gurmukhi MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Gurmukhi", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(si) { + --RS__baseFontFamily: "Sinhala Sangam MN", "Nirmala UI", "Iskoola Pota", Roboto, Noto, "Noto Sans Sinhala", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ta) { + --RS__baseFontFamily: "Tamil Sangam MN", "Nirmala UI", Latha, Roboto, Noto, "Noto Sans Tamil", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(te) { + --RS__baseFontFamily: "Kohinoor Telugu", "Telugu Sangam MN", "Nirmala UI", Gautami, Roboto, Noto, "Noto Sans Telugu", sans-serif; +} + +:lang(th) { + --RS__baseFontFamily: "Thonburi", "Leelawadee UI", "Cordia New", Roboto, Noto, "Noto Sans Thai", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +/* The following will also work for zh-Hans */ + +:lang(zh) { + --RS__baseFontFamily: "方体", "PingFang SC", "黑体", "Heiti SC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK SC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-Hant), +:lang(zh-TW) { + --RS__baseFontFamily: "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-HK) { + --RS__baseFontFamily: "方體", "PingFang HK", "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +/* Readium CSS + Day/Default mode + + A preset theme for day mode, which is the default + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__backgroundColor: #FFFFFF; + --RS__textColor: #121212; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +:root { + color: var(--RS__textColor) !important; + background-color: var(--RS__backgroundColor) !important; +} + +/* Note: Though `::selection` was present in drafts of CSS Selectors Level 3, it was removed during the Candidate Recommendation phase because its behavior was under-specified (especially with nested elements) and interoperability wasn’t achieved. Source: https://developer.mozilla.org/en-US/docs/Web/CSS/::selection */ + +::-moz-selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +::selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +/* @import "modules/ReadiumCSS-fonts.css"; */ + +/* Readium CSS + HTML5 SR Patch stylesheet + + A set of style to adjust HTML5 Suggested Rendering to paginated content + + Repo: https://github.com/readium/readium-css */ + +/* Fragmentation */ + +body { + widows: 2; + orphans: 2; +} + +figcaption, th, td { + widows: 1; + orphans: 1; +} + +h2, h3, h4, h5, h6, dt, +hr, caption { + -webkit-column-break-after: avoid; + page-break-after: avoid; + break-after: avoid; +} + +h1, h2, h3, h4, h5, h6, dt, +figure, tr { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Hyphenation */ + +body { + -webkit-hyphenate-character: "\002D"; + -moz-hyphenate-character: "\002D"; + -ms-hyphenate-character: "\002D"; + hyphenate-character: "\002D"; + -webkit-hyphenate-limit-lines: 3; + -ms-hyphenate-limit-lines: 3; + hyphenate-limit-lines: 3; +} + +h1, h2, h3, h4, h5, h6, dt, +figcaption, pre, caption, address, +center, code, var { + -ms-hyphens: none; + -moz-hyphens: none; + -webkit-hyphens: none; + -epub-hyphens: none; + hyphens: none; +} + +/* OTF */ + +body { + font-variant-numeric: oldstyle-nums proportional-nums; +} + +:lang(ja) body, +:lang(zh) body, +:lang(ko) body { + font-variant-numeric: lining-nums proportional-nums; +} + +h1, h2, h3, h4, h5, h6, dt { + font-variant-numeric: lining-nums proportional-nums; +} + +table { + font-variant-numeric: lining-nums tabular-nums; +} + +code, var { + font-variant-ligatures: none; + font-variant-numeric: lining-nums tabular-nums slashed-zero; +} + +rt { + font-variant-east-asian: ruby; +} + +:lang(ar) { + font-variant-ligatures: common-ligatures; +} + +:lang(ko) { + font-kerning: normal; +} + +/* Night mode */ + +hr { + color: inherit; + border-color: currentColor; +} + +table, th, td { + border-color: currentColor; +} + +/* Horizontal Spacing */ + +figure, blockquote { + margin: 1em 5%; +} + +/* + +:lang(ja) figure, :lang(ja) blockquote, +:lang(zh-Hant) figure, :lang(zh-Hant) blockquote, +:lang(zh-TW) figure, :lang(zh-TW) blockquote, +:lang(mn) figure, :lang(mn) blockquote { + margin: 5% 1em; +} + +*/ + +ul, ol { + padding-left: 5%; +} + +/* + +:lang(ja) ul, :lang(ja) ol, +:lang(zh-Hant) ul, :lang(zh-Hant) ol, +:lang(zh-TW) ul, :lang(zh-TW) ol, +:lang(mn) ul, :lang(mn) ol { + padding-top: 5%; +} + +*/ + +dd { + margin-left: 5%; +} + +/* + +:lang(ja) dd, +:lang(zh-Hant) dd, +:lang(zh-TW) dd, +:lang(mn) dd { + margin-top: 5%; +} + +*/ + +pre { + white-space: pre-wrap; + -ms-tab-size: 2; + -moz-tab-size: 2; + -webkit-tab-size: 2; + tab-size: 2; +} + +/* Normalization */ + +abbr[title], acronym[title] { + text-decoration: dotted underline; +} + +nobr wbr { + white-space: normal; +} + +/* Make ruby text and parentheses non-selectable (TBC) */ + +ruby > rt, ruby > rp { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Internationalization */ + +*:lang(ja), +*:lang(zh), +*:lang(ko), +:lang(ja) cite, :lang(ja) dfn, :lang(ja) em, :lang(ja) i, +:lang(zh) cite, :lang(zh) dfn, :lang(zh) em, :lang(zh) i, +:lang(ko) cite, :lang(ko) dfn, :lang(ko) em, :lang(ko) i { + font-style: normal; +} + +:lang(ja) a, +:lang(zh) a, +:lang(ko) a { + text-decoration: none; +} + +/* Readium CSS + Safeguards module for vertical writing + + A set of styles to prevent common issues in pagination + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* We’ll be using an "RS__" prefix so that we can prevent collisions with authors’ CSS */ + +:root { + /* max-width for media, you can override that via JS if not compiled to static */ + --RS__maxMediaWidth: 100%; + + /* max-height for media, you can override that via JS if not compiled to static */ + --RS__maxMediaHeight: 100vw; + + /* value for medias’ box-sizing */ + --RS__boxSizingMedia: border-box; + + /* value for table’s box-sizing */ + --RS__boxSizingTable: border-box; +} + +/* Sanitize line-heights in webkit e.g. raised cap without a declared line-height + See effect by checking this demo in Safari: https://codepen.io/JayPanoz/pen/gRmzrE + Note: glyphs has to be reset to inline for CJK */ + +html { + -webkit-line-box-contain: block glyphs replaced; +} + +:lang(ja) { + -webkit-line-box-contain: block inline replaced; +} + +/* Wrap long strings if larger than line-length */ + +a, h1, h2, h3, h4, h5, h6 { + word-wrap: break-word; +} + +div { + max-width: var(--RS__maxMediaHeight); +} + +/* Size medias */ + +/* You can override CSS variables by re-defining it for all elements or a specific one */ + +img, svg, audio, video { + + /* Object-fit allows us to keep the correct aspect-ratio */ + object-fit: contain; + + width: auto; + height: auto; + + /* We’re setting a max-height, especially for covers */ + max-width: var(--RS__maxMediaHeight); + + /* Some files don’t have max-width */ + max-height: var(--RS__maxMediaWidth) !important; + /* We probably don’t need to use modern box-sizing as auto behaves like it */ + box-sizing: var(--RS__boxSizingMedia); + + /* For page-break, we must use those 3 + We can’t use a variable there, webkit seems to no support them for those properties */ + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Try preventing border being cut-off, webkit + blink have content-box by default */ + +table { + max-height: var(--RS__maxMediaWidth); + box-sizing: var(--RS__boxSizingTable) +} +/*# sourceMappingURL=ReadiumCSS-before.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-default.css b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-default.css new file mode 100644 index 00000000..6f9d326e --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/cjk-vertical/ReadiumCSS-default.css @@ -0,0 +1,185 @@ +/* Readium CSS + Default module for CJK vertical writing + + A stylesheet for unstyled ebooks based on HTML5 Suggested Rendering + Note: works in combination with Base module + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + /* Extra variables for Japanese font-stacks: + • --RS__serif-ja-v; + • --RS__sans-serif-ja-v. + + They can be used instead of --RS__baseFontFamily and --RS__compFontFamily */ + + --RS__compFontFamily: var(--RS__baseFontFamily); + --RS__codeFontFamily: var(--RS__monospaceTf); + + --RS__typeScale: 1.125; /* 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618 */ + --RS__baseFontSize: 87.5%; + + --RS__flowSpacing: 1.5rem; + --RS__paraSpacing: 0; + --RS__paraIndent: 1em; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + --RS__primaryColor: ; + --RS__secondaryColor: ; +} + +:lang("mn-Mong") { + --RS__baseFontSize: 100%; +} + +/* STYLES */ + +/* Typo */ + +body { + font-size: var(--RS__baseFontSize); + text-align: justify; + text-justify: inter-character; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--RS__baseFontFamily); + text-align: left; + text-align: start; +} + +/* Flow content */ + +blockquote, figure, p, pre, +aside, footer, form, hr { + margin-right: var(--RS__flowSpacing); + margin-left: var(--RS__flowSpacing); +} + +p { + margin-right: var(--RS__paraSpacing); + margin-left: var(--RS__paraSpacing); + text-indent: var(--RS__paraIndent); +} + +pre { + font-family: var(--RS__codeFontFamily); +} + +/* Phrasing content */ + +code, kbd, samp, tt { + font-family: var(--RS__codeFontFamily); +} + +sub, sup { + position: relative; + font-size: 67.5%; + line-height: 1; +} + +sub { + left: -0.2ex; +} + +sup { + right: 0; +} + +em { + -webkit-text-emphasis: sesame; + -epub-text-emphasis: sesame; + text-emphasis: sesame; +} + +:link { + color: var(--RS__linkColor); +} + +:visited { + color: var(--RS__visitedColor); +} + +/* Headings */ + +h1 { + margin-right: calc(var(--RS__flowSpacing) * 2); + margin-left: calc(var(--RS__flowSpacing) * 2); + /* The following is base font size * typescale power of 3 */ + font-size: calc(((1em * var(--RS__typeScale)) * var(--RS__typeScale)) * var(--RS__typeScale)); + text-indent: 2rem; +} + +h2 { + margin-right: calc(var(--RS__flowSpacing) * 2); + margin-left: var(--RS__flowSpacing); + /* The following is base font size * typescale power of 2 */ + font-size: calc((1em * var(--RS__typeScale)) * var(--RS__typeScale)); + text-indent: 3rem; +} + +h3 { + margin-right: var(--RS__flowSpacing); + margin-left: var(--RS__flowSpacing); + font-size: calc(1em * var(--RS__typeScale)); + text-indent: 4rem; +} + +h4 { + margin-right: var(--RS__flowSpacing); + margin-left: var(--RS__flowSpacing); + font-family: var(--RS__compFontFamily); + font-size: 1em; + text-indent: 4rem; +} + +h5 { + margin-right: var(--RS__flowSpacing); + margin-left: var(--RS__flowSpacing); + font-family: var(--RS__compFontFamily); + font-size: smaller; + text-indent: 4rem; +} + +h6 { + margin-right: var(--RS__flowSpacing); + margin-left: 0; + font-family: var(--RS__compFontFamily); + font-size: smaller; + font-weight: normal; + text-indent: 4rem; +} + +/* Lists */ + +dl, ol, ul { + margin-right: var(--RS__flowSpacing); + margin-left: var(--RS__flowSpacing); +} + +/* Table */ + +table { + margin: 0 var(--RS__flowSpacing); + border: 1px solid currentColor; + border-collapse: collapse; + empty-cells: show; +} + +thead, tbody, tfoot, table > tr { + vertical-align: top; +} + +th { + text-align: left; +} + +th, td { + padding: 4px; + border: 1px solid currentColor; +} +/*# sourceMappingURL=ReadiumCSS-default.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-after.css b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-after.css new file mode 100644 index 00000000..69384a34 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-after.css @@ -0,0 +1,906 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Pagination module + + A set of styles to paginate ePublications + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* Columns are responsive by default, even if column-width is set in pixels, + which means two-page spread will switch to single page depending on current font-size. + If you want more control, I’m afraid you’ll have to update colWidth/colGap dynamically, + which is how a significant amount of RS do at the moment. */ + +/* Default for smartphone portrait (small screens) */ + +:root { + /* Your columns’ width floor */ + --RS__colWidth: 45em; /* The width at which we’ll switch to 2 columns by default. PS: you can’t set it in rem */ + + /* Ideal number of columns (depending on columns’ width floor) */ + --RS__colCount: 1; + + /* Gap between columns (in pixels so that it won’t resize with font-size) */ + --RS__colGap: 0; + + /* Optimal line-length (rem will take :root font-size into account, whatever the body’s font-size) */ + --RS__maxLineLength: 40rem; + + /* Default page horizontal margins (in pixels so that it won’t resize with font-size) */ + --RS__pageGutter: 20px; /* See if colGap and pageGutter can be the same var */ +} + +/* Reset page margins for Forward compatibility */ + +@page { + margin: 0 !important; +} + +/* :root selector has same specificity as a class i.e. 0010 + We might have to change that to html / context + -> https://css-tricks.com/almanac/selectors/r/root/ */ + +:root { + + /* In case you use left position to scroll, can be removed if using transform: translateX() */ + position: relative; + + -webkit-column-width: var(--RS__colWidth); + -moz-column-width: var(--RS__colWidth); + column-width: var(--RS__colWidth); + + /* Init pagination */ + /* TODO: document columns’ logic cos it might feel weird at first */ + -webkit-column-count: var(--RS__colCount); + -moz-column-count: var(--RS__colCount); + column-count: var(--RS__colCount); + + -webkit-column-gap: var(--RS__colGap); + -moz-column-gap: var(--RS__colGap); + column-gap: var(--RS__colGap); + + /* Default is balance and we want columns to be filled entirely (100vh) */ + -moz-column-fill: auto; + column-fill: auto; + width: 100%; + height: 100vh; + max-width: 100%; + max-height: 100vh; + min-width: 100%; + min-height: 100vh; + padding: 0 !important; + margin: 0 !important; + + /* Column size will depend on this if we want to make it responsive */ + font-size: 100% !important; + + -webkit-text-size-adjust: 100%; + + /* Switch to newer box model (not inherited by authors’ styles) */ + box-sizing: border-box; + + /* Fix bug for older Chrome */ + -webkit-perspective: 1; + /* Prevents options pop-up when long tap in webkit */ + -webkit-touch-callout: none; +} + +body { + /* overflow: hidden; bugfix: contents won’t paginate in Firefox and one sample in Safari */ + width: 100%; + + /* Limit line-length but we have to reset when 2 columns and control the viewport. + By using max-width + margin auto, margins will shrink when font-size increases, + which is what would be expected in terms of typography. */ + max-width: var(--RS__maxLineLength) !important; + padding: 0 var(--RS__pageGutter) !important; + margin: 0 auto !important; + + /* We need a minimum padding on body so that descandants/ascendants in italic/script are not cut-off. + Drawback: we have to use border-box so that it doesn’t screw the box model, + which means it impacts colWidth and max-width */ + box-sizing: border-box; +} + +/* We’ll now redefine margins and columns depending on the minimum width available + The goal is having the simplest model possible and avoid targeting devices */ + +/* Smartphone landscape */ + +@media screen and (min-width: 35em) { + :root { + --RS__pageGutter: 30px; + } +} + +/* Tablet portrait */ + +@media screen and (min-width: 45em) { + :root { + --RS__pageGutter: 40px; + } +} + +/* Desktop + tablet large */ + +/* We get the previous settings, we just change the margins */ + +@media screen and (min-width: 75em) { + :root { + --RS__pageGutter: 50px; + } +} + +/* At this point (80em or so), constraining line length must be done at the web view/iframe level, or by limiting the size of :root itself */ + +/* Responsive columns */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root { + /* The size at which we want 2 columns to switch to 1 (depending on font-size) */ + --RS__colWidth: 20em; /* 20 * 16 = 320px but 20 * 28 = 560px so it will switch to 1 col @ 175% font-size (user-setting) on an iPad */ + /* We constrain to 2 columns so that we can never get 3 or 4, etc. */ + --RS__colCount: 2; + --RS__maxLineLength: 39.99rem; /* If we don’t use this, colNumber user setting won’t work in Safari… */ + } +} + +/* Readium CSS + Scroll module + + A set of styles to scroll ePublications + This module overrides pagination + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-scroll-on"] { + + /* Reset columns, auto + auto = columns can’t be created */ + -webkit-columns: auto auto !important; + -moz-columns: auto auto !important; + columns: auto auto !important; + width: auto !important; + height: auto !important; + max-width: none !important; + max-height: none !important; + /* Reset html size so that the user can scroll */ + min-width: 0 !important; + min-height: 0 !important; +} + +/* Make sure line-length is limited in all configs */ + +:root[style*="readium-scroll-on"] body { + --RS__maxLineLength: 40rem !important; + + margin-top: var(--RS__pageGutter) !important; + margin-bottom: var(--RS__pageGutter) !important; +} + +/* Scroll mode horizontal */ + +/* Vertical writing needs body height set */ + +/* Do we add a top/bottom margin for body in vertical scroll or not? */ + +/* Readium CSS + Default highlights + + A stylesheet for user highlights + + Repo: https://github.com/readium/readium-css */ + +/* User Highlights */ + +.readiumCSS-yellow-highlight { + background-color: rgba(255, 255, 0, 0.5); +} + +.readiumCSS-green-highlight { + background-color: rgba(0, 255, 0, 0.5); +} + +.readiumCSS-orange-highlight { + background-color: rgba(255, 165, 0, 0.5); +} + +.readiumCSS-pink-highlight { + background-color: rgba(255, 105, 180, 0.5); +} + +/* Media overlays */ + +.readiumCSS-mo-active-default { + color: black !important; + background-color: yellow !important; +} + +/* Readium CSS + Night mode + + A preset theme for night mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +/* [style*="--USER__appearance"] can be used to increase specificity but performance hit */ + +:root[style*="readium-night-on"] { + --RS__backgroundColor: #000000; + --RS__textColor: #FEFEFE; + + --RS__linkColor: #63caff; + --RS__visitedColor: #0099E5; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-night-on"] *:not(a) { + color: inherit !important; + background-color: transparent !important; + border-color: currentColor !important; +} + +:root[style*="readium-night-on"] svg text { + fill: currentColor !important; + stroke: none !important; +} + +:root[style*="readium-night-on"] a:link, +:root[style*="readium-night-on"] a:link * { + color: var(--RS__linkColor) !important; +} + +:root[style*="readium-night-on"] a:visited, +:root[style*="readium-night-on"] a:visited * { + color: var(--RS__visitedColor) !important; +} + +:root[style*="readium-night-on"] img[class*="gaiji"], +:root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); +} + +/* Invert all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken and invert on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%) invert(100%); + filter: brightness(80%) invert(100%); +} + +/* Readium CSS + Sepia mode + + A preset theme for sepia mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root[style*="readium-sepia-on"] { + --RS__backgroundColor: #faf4e8; + --RS__textColor: #121212; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; + + --RS__maxLineLength: 40.01rem; /* Forcing a reflow in Blink/Webkit so that blend mode can work */ +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-sepia-on"] body { + /* Should be transparent but Chrome bug https://bugs.chromium.org/p/chromium/issues/detail?id=711955&q=mix-blend-mode&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified */ + + color: inherit; + background-color: var(--RS__backgroundColor); +} + +:root[style*="readium-sepia-on"] a:link, +:root[style*="readium-sepia-on"] a:link * { + color: var(--RS__linkColor); +} + +:root[style*="readium-sepia-on"] a:visited, +:root[style*="readium-sepia-on"] a:visited * { + color: var(--RS__visitedColor); +} + +:root[style*="readium-sepia-on"] svg, +:root[style*="readium-sepia-on"] img { + /* Make sure the proper bg-color is used for the blend mode */ + background-color: transparent !important; + mix-blend-mode: multiply; +} + +/* Readium CSS + OS Accessibility Modes + + A stylesheet to deal with OS accessibility settings + + Repo: https://github.com/readium/readium-css */ + +/* Windows high contrast colors are mapped to CSS system color keywords + See http://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast */ + +@media screen and (-ms-high-contrast: active) { + :root { + color: windowText !important; + background-color: window !important; + } + + /* The following selectors are super funky but it makes sure everything is inherited, this is indeed critical for this mode */ + :root :not(#\#):not(#\#):not(#\#), + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) { + color: inherit !important; + background-color: inherit !important; + } + + .readiumCSS-mo-active-default { + color: highlightText !important; + background-color: highlight !important; + } + + /* For links, hyperlink keyword is automatically set */ + + /* Should we also set user highlights? */ +} + +@media screen and (-ms-high-contrast: white-on-black) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +/* Will be true on recent versions of iOS and MacOS if inverted setting enabled by the user */ + +@media screen and (inverted-colors) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +@media screen and (monochrome) { + /* Grayscale (Implemented in Safari, what about eInk?) */ + /* Must deal with anything color (contrast) so must be managed at the night/sepia/theme level :( */ +} + +@media screen and (prefers-reduced-motion) { + /* If reduced motion is set on MacOS, in case we have animation/transition */ +} + +/* Readium CSS + Columns number pref + + A submodule managing columns number for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Number of columns = 1 | 2 */ + +/* We still need to see if we allow users to force number of columns for all configs, currently it behaves as an "auto" setting */ + +/* apply col setting except for mobile portrait */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"], + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + -webkit-column-count: var(--USER__colCount); + -moz-column-count: var(--USER__colCount); + column-count: var(--USER__colCount); + } + + /* If one column, make sure we limit line-length */ + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"] { + --RS__maxLineLength: 40rem !important; /* This is the only way for the user setting to work in webkit… */ + --RS__colWidth: 100vw; + } + + /* If smartphone landscape, and 2 columns, col width the same as iPad landscape + desktop */ + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + --RS__colWidth: auto; /* User explicitely tells he/she wants 2 columns, we reset floor value */ + } +} + +/* Readium CSS + Page margins pref + + A submodule managing page margins for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Page Margins: the user margin is a factor of the page gutter e.g. 0.5, 0.75, 1, 1.25, 1.5, etc. */ + +:root[style*="--USER__pageMargins"] body { + padding: 0 calc(var(--RS__pageGutter) * var(--USER__pageMargins)) !important; +} + +/* Readium CSS + Custom colors pref + + A submodule managing custom colors for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__backgroundColor"] { + background-color: var(--USER__backgroundColor) !important; +} + +:root[style*="--USER__backgroundColor"] * { + background-color: transparent !important; +} + +:root[style*="--USER__textColor"] { + color: var(--USER__textColor) !important; +} + +:root[style*="--USER__textColor"] *:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) { + color: inherit !important; +} + +/* Readium CSS + Text align pref + + A submodule managing text-align for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] { + text-align: var(--USER__textAlign); +} + +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] *:not(blockquote):not(figcaption) p, +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] li { + text-align: inherit !important; + -moz-text-align-last: auto !important; + -epub-text-align-last: auto !important; + text-align-last: auto !important; +} + +/* In case something goes wrong at the programmatic level + rtl for body + rtl in ltr */ + +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign: left"], +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign:left"], +:root[style*="readium-advanced-on"][style*="--USER__textAlign: left"] *[dir="rtl"], +:root[style*="readium-advanced-on"][style*="--USER__textAlign:left"] *[dir="rtl"] { + text-align: right; +} + +/* Edge, if logical value is used, think of it as a polyfill. For LTR, it will fall back to the default, which is left */ + +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign: start"], +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign:start"] { + text-align: right; +} + +/* Readium CSS + Hyphenation pref + + A submodule managing hyphens for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Managing hyphenation automatically for text-align values */ + +:root[style*="readium-advanced-on"][style*="--USER__textAlign: justify"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign:justify"] body { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + -epub-hyphens: auto; + hyphens: auto; +} + +:root[style*="readium-advanced-on"][style*="--USER__textAlign: left"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign:left"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign: right"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign:right"] body { + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + -epub-hyphens: none; + hyphens: none; +} + +/* Managing the user override */ + +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] { + -webkit-hyphens: var(--USER__bodyHyphens) !important; + -moz-hyphens: var(--USER__bodyHyphens) !important; + -ms-hyphens: var(--USER__bodyHyphens) !important; + -epub-hyphens: var(--USER__bodyHyphens) !important; + hyphens: var(--USER__bodyHyphens) !important; +} + +/* Sorry, we can’t use `:matches`, `:-moz-any` or `:-webkit-any` because MS doesn’t support it yet */ + +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] body, +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] p, +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] li, +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] div, +:root[style*="readium-advanced-on"][style*="--USER__bodyHyphens"] dd { + -webkit-hyphens: inherit; + -moz-hyphens: inherit; + -ms-hyphens: inherit; + -epub-hyphens: inherit; + hyphens: inherit; +} + +/* Readium CSS + Font Family pref + + A submodule managing font-family for user settings + Part of “User Overrides” class – “font override” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] { + font-family: var(--USER__fontFamily) !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] body, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] p, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] li, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] div, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dt, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dd { + font-family: inherit !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([xml\:lang]) { + font-family: inherit !important; +} + +/* Readium CSS + A11y font pref + + A submodule managing a11y text normalization for user settings + Part of “User Overrides” class – “font override” flag required. + + Repo: https://github.com/readium/readium-css */ + +/* For AccessibleDfa, we need to normalize font-weight and font-style since only the normal style is available */ + +:root[style*="readium-font-on"][style*="AccessibleDfa"] { + /* We won’t use the variable there since we need fallbacks for missing characters */ + font-family: AccessibleDfa, Verdana, Tahoma, "Trebuchet MS", sans-serif !important; + --RS__lineHeightCompensation: 1.167; +} + +:root[style*="readium-font-on"][style*="IA Writer Duospace"] { + /* We won’t use the variable there since we need fallbacks for missing characters */ + font-family: "IA Writer Duospace", Menlo, "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Courier, monospace !important; + --RS__lineHeightCompensation: 1.167; +} + +:root[style*="readium-font-on"][style*="readium-a11y-on"] { + font-family: var(--USER__fontFamily) !important; + --RS__lineHeightCompensation: 1.167; +} + +/* Maybe users want a setting to normalize any font offered so there is a “a11y Normalize” flag for it */ + +:root[style*="readium-font-on"][style*="AccessibleDfa"], +:root[style*="readium-font-on"][style*="IA Writer Duospace"], +:root[style*="readium-font-on"][style*="readium-a11y-on"] { + font-style: normal !important; + font-weight: normal !important; +} + +/* Targeting everything except code. Note that Open Dyslexic has a monospaced font for code */ + +:root[style*="readium-font-on"][style*="AccessibleDfa"] *:not(code):not(var):not(kbd):not(samp), +:root[style*="readium-font-on"][style*="IA Writer Duospace"] *:not(code):not(var):not(kbd):not(samp), +:root[style*="readium-font-on"][style*="readium-a11y-on"] *:not(code):not(var):not(kbd):not(samp) { + font-family: inherit !important; + font-style: inherit !important; + font-weight: inherit !important; +} + +/* Normalizing text-decoration, subs and sups */ + +:root[style*="readium-font-on"][style*="AccessibleDfa"] *, +:root[style*="readium-font-on"][style*="IA Writer Duospace"] *, +:root[style*="readium-font-on"][style*="readium-a11y-on"] * { + text-decoration: none !important; + font-variant-caps: normal !important; + font-variant-numeric: normal !important; + font-variant-position: normal !important; +} + +:root[style*="readium-font-on"][style*="AccessibleDfa"] sup, +:root[style*="readium-font-on"][style*="IA Writer Duospace"] sup, +:root[style*="readium-font-on"][style*="readium-a11y-on"] sup, +:root[style*="readium-font-on"][style*="AccessibleDfa"] sub, +:root[style*="readium-font-on"][style*="IA Writer Duospace"] sub, +:root[style*="readium-font-on"][style*="readium-a11y-on"] sub { + font-size: 1rem !important; + vertical-align: baseline !important; +} + +/* Readium CSS + Font size pref + + A submodule managing font-size for user settings + Part of “User Overrides” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__fontSize"] { + font-size: var(--USER__fontSize) !important; +} + +/* Readium CSS + Line height pref + + A submodule managing line-height for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] { + line-height: var(--USER__lineHeight) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] body, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] p, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] li, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] div { + line-height: inherit; +} + +/* Readium CSS + Para spacing pref + + A submodule managing paragraphs’ top and bottom margins for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraSpacing"] p { + margin-top: var(--USER__paraSpacing) !important; + margin-bottom: var(--USER__paraSpacing) !important; +} + +/* Readium CSS + Para indent pref + + A submodule managing paragraphs’ text-indent for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p { + text-indent: var(--USER__paraIndent) !important; +} + +/* If there are inline-block elements in paragraphs, text-indent will inherit so we must reset it */ + +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p *, +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p:first-letter { + text-indent: 0 !important; +} + +/* Readium CSS + Word spacing pref + + A submodule managing word-spacing for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h1, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h2, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h3, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h4, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h5, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] h6, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] p, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] li, +:root[style*="readium-advanced-on"][style*="--USER__wordSpacing"] div { + word-spacing: var(--USER__wordSpacing); +} + +/* Readium CSS + Letter spacing pref + + A submodule managing letter-spacing for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h1, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h2, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h3, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h4, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h5, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] h6, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] p, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] li, +:root[style*="readium-advanced-on"][style*="--USER__letterSpacing"] div { + letter-spacing: var(--USER__letterSpacing); + font-variant: none; +} + +/* Readium CSS + Font size normalize + + A stylesheet to normalize font-size + + Repo: https://github.com/readium/readium-css */ + +/* STYLES */ + +/* :root is used so that you can quickly add a class or attribute if you prefer e.g. `:root[data-rs-normalize]` */ + +/* We create a default so that you don’t need to explicitly set one in the DOM. + Once the “Publisher’s styles” checkbox is unchecked, the normalize is applied automatically */ + +:root[style*="readium-advanced-on"] { + --USER__typeScale: 1.2; /* This is the default type scale you’ll find in most publications */ +} + +:root[style*="readium-advanced-on"] p, +:root[style*="readium-advanced-on"] li, +:root[style*="readium-advanced-on"] div, +:root[style*="readium-advanced-on"] pre, +:root[style*="readium-advanced-on"] dd { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] h1 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.75rem !important; + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h2 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.5rem !important; + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h3 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.25rem !important; + font-size: calc(1rem * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h4, +:root[style*="readium-advanced-on"] h5, +:root[style*="readium-advanced-on"] h6 { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] small { + font-size: smaller !important; +} + +:root[style*="readium-advanced-on"] sub, +:root[style*="readium-advanced-on"] sup { + font-size: 67.5% !important; +} + +/* The following styles kick in if you define the typeScale variable in the DOM. + No need to repeat declarations which don’t make use of the variable */ + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h1 { + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h2 { + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h3 { + font-size: calc(1rem * var(--USER__typeScale)) !important; +} +/*# sourceMappingURL=ReadiumCSS-after.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-before.css b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-before.css new file mode 100644 index 00000000..1f91d4ee --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-before.css @@ -0,0 +1,606 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Base module + + A minimal stylesheet for all ebooks + + Repo: https://github.com/readium/readium-css */ + +@namespace url("http://www.w3.org/1999/xhtml"); + +@namespace epub url("http://www.idpf.org/2007/ops"); + +@namespace m url("http://www.w3.org/1998/Math/MathML/"); + +@namespace svg url("http://www.w3.org/2000/svg"); + +/* Define viewport, HTML5-style */ + +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; + zoom: 1; +} + +:root { + /* Default font-stacks */ + --RS__oldStyleTf: "Iowan Old Style", "Sitka Text", Palatino, "Book Antiqua", serif; + --RS__modernTf: Athelas, Constantia, Georgia, serif; + --RS__sansTf: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --RS__humanistTf: Seravek, Calibri, Roboto, Arial, sans-serif; + --RS__monospaceTf: "Andale Mono", Consolas, monospace; + + /* Config */ + --RS__baseFontFamily: var(--RS__oldStyleTf); + + /* For square-ish fonts (CJK, Indic, etc.), we must apply some compensation in dynamic leading. Default is 1 i.e. no compensation */ + --RS__lineHeightCompensation: 1; + + /* Dynamic leading based on typeface metrics + font-size setting */ + --RS__baseLineHeight: calc((1em + (2ex - 1ch) - ((1rem - 16px) * 0.1667)) * var(--RS__lineHeightCompensation)); +} + +/* Set default font for the html doc, so that it can be overridden by the authors’s stylesheet */ + +html { + font-family: var(--RS__baseFontFamily); + /* Fallback line-height */ + line-height: 1.6; /* Fits a little bit better for all languages than 1.5 */ + line-height: var(--RS__baseLineHeight); + text-rendering: optimizeLegibility; +} + +/* 1.5 being too loose with larger font-sizes, we reset headings to normal (which value is 1.125–1.375 for our font-stacks) */ + +h1, h2, h3 { + line-height: normal; +} + +:lang(ja), +:lang(zh), +:lang(ko) { + word-wrap: break-word; + -webkit-line-break: strict; + -epub-line-break: strict; + line-break: strict; +} + +/* Set default font for Math */ + +math { + font-family: "Latin Modern Math", "STIX Two Math", "XITS Math", "STIX Math", "Libertinus Math", "TeX Gyre Termes Math", "TeX Gyre Bonum Math", "TeX Gyre Schola", "DejaVu Math TeX Gyre", "TeX Gyre Pagella Math", "Asana Math", "Cambria Math", "Lucida Bright Math", "Minion Math", STIXGeneral, STIXSizeOneSym, Symbol, "Times New Roman", serif; +} + +/* Language Overrides + That will only work if either html or body have a (xml:)lang attribute, not for inline overrides */ + +:lang(am) { + --RS__baseFontFamily: Kefa, Nyala, Roboto, Noto, "Noto Sans Ethiopic", serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ar) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(bn) { + --RS__baseFontFamily: "Kohinoor Bangla", "Bangla Sangam MN", Vrinda, Roboto, Noto, "Noto Sans Bengali", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(bo) { + --RS__baseFontFamily: Kailasa, "Microsoft Himalaya", Roboto, Noto, "Noto Sans Tibetan", sans-serif; +} + +:lang(chr) { + --RS__baseFontFamily: "Plantagenet Cherokee", Roboto, Noto, "Noto Sans Cherokee"; + --RS__lineHeightCompensation: 1.167; +} + +:lang(fa) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(gu) { + --RS__baseFontFamily: "Gujarati Sangam MN", "Nirmala UI", Shruti, Roboto, Noto, "Noto Sans Gujarati", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(he) { + --RS__baseFontFamily: "New Peninim MT", "Arial Hebrew", Gisha, "Times New Roman", Roboto, Noto, "Noto Sans Hebrew" sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(hi) { + --RS__baseFontFamily: "Kohinoor Devanagari", "Devanagari Sangam MN", Kokila, "Nirmala UI", Roboto, Noto, "Noto Sans Devanagari", sans-serif; + + --RS__lineHeightCompensation: 1.1; +} + +:lang(hy) { + --RS__baseFontFamily: Mshtakan, Sylfaen, Roboto, Noto, "Noto Serif Armenian", serif; +} + +:lang(iu) { + --RS__baseFontFamily: "Euphemia UCAS", Euphemia, Roboto, Noto, "Noto Sans Canadian Aboriginal", sans-serif; +} + +:lang(ja) { + --RS__baseFontFamily: "游ゴシック体", YuGothic, "ヒラギノ丸ゴ", "Hiragino Sans", "Yu Gothic UI", "Meiryo UI", "MS Gothic", Roboto, Noto, "Noto Sans CJK JP", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; + + /* Extra variables for Japanese font-stacks as we may want to reuse them for user settings + default */ + --RS__serif-ja: "MS P明朝", "MS PMincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS 明朝", "MS Mincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja: "MS Pゴシック", "MS PGothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS ゴシック", "MS Gothic", "Hiragino Sans", sans-serif; + --RS__serif-ja-v: "MS 明朝", "MS Mincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS P明朝", "MS PMincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja-v: "MS ゴシック", "MS Gothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS Pゴシック", "MS PGothic", "Hiragino Sans", sans-serif; +} + +:lang(km) { + --RS__baseFontFamily: "Khmer Sangam MN", "Leelawadee UI", "Khmer UI", Roboto, Noto, "Noto Sans Khmer", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(kn) { + --RS__baseFontFamily: "Kannada Sangam MN", "Nirmala UI", Tunga, Roboto, Noto, "Noto Sans Kannada", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(ko) { + --RS__baseFontFamily: "Nanum Gothic", "Apple SD Gothic Neo", "Malgun Gothic", Roboto, Noto, "Noto Sans CJK KR", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(lo) { + --RS__baseFontFamily: "Lao Sangam MN", "Leelawadee UI", "Lao UI", Roboto, Noto, "Noto Sans Lao", sans-serif; +} + +:lang(ml) { + --RS__baseFontFamily: "Malayalam Sangam MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Malayalam", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(or) { + --RS__baseFontFamily: "Oriya Sangam MN", "Nirmala UI", Kalinga, Roboto, Noto, "Noto Sans Oriya", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(pa) { + --RS__baseFontFamily: "Gurmukhi MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Gurmukhi", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(si) { + --RS__baseFontFamily: "Sinhala Sangam MN", "Nirmala UI", "Iskoola Pota", Roboto, Noto, "Noto Sans Sinhala", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ta) { + --RS__baseFontFamily: "Tamil Sangam MN", "Nirmala UI", Latha, Roboto, Noto, "Noto Sans Tamil", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(te) { + --RS__baseFontFamily: "Kohinoor Telugu", "Telugu Sangam MN", "Nirmala UI", Gautami, Roboto, Noto, "Noto Sans Telugu", sans-serif; +} + +:lang(th) { + --RS__baseFontFamily: "Thonburi", "Leelawadee UI", "Cordia New", Roboto, Noto, "Noto Sans Thai", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +/* The following will also work for zh-Hans */ + +:lang(zh) { + --RS__baseFontFamily: "方体", "PingFang SC", "黑体", "Heiti SC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK SC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-Hant), +:lang(zh-TW) { + --RS__baseFontFamily: "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-HK) { + --RS__baseFontFamily: "方體", "PingFang HK", "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +/* Readium CSS + Day/Default mode + + A preset theme for day mode, which is the default + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__backgroundColor: #FFFFFF; + --RS__textColor: #121212; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +:root { + color: var(--RS__textColor) !important; + background-color: var(--RS__backgroundColor) !important; +} + +/* Note: Though `::selection` was present in drafts of CSS Selectors Level 3, it was removed during the Candidate Recommendation phase because its behavior was under-specified (especially with nested elements) and interoperability wasn’t achieved. Source: https://developer.mozilla.org/en-US/docs/Web/CSS/::selection */ + +::-moz-selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +::selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +/* Readium CSS + Fonts module + + A stylesheet for embedded fonts + + Repo: https://github.com/readium/readium-css */ + +/* /!\ Mind the path (relative to the folders in which you have stylesheets and the fonts) */ + +@font-face { + font-family: AccessibleDfa; + font-style: normal; + font-weight: normal; + src: local("AccessibleDfa"), + url("fonts/AccessibleDfA.otf") format("opentype"); +} + +@font-face { + font-family: "IA Writer Duospace"; + font-style: normal; + font-weight: normal; + src: local("iAWriterDuospace-Regular"), + url("fonts/iAWriterDuospace-Regular.ttf") format("truetype"); +} + +/* If you have different weights/styles, + use `font-weight` and `font-style`, + not prefixes in the font-family name, + or else it will be a nightmare to manage in user settings. */ + +/* Readium CSS + HTML5 SR Patch stylesheet + + A set of style to adjust HTML5 Suggested Rendering to paginated content + + Repo: https://github.com/readium/readium-css */ + +/* Fragmentation */ + +body { + widows: 2; + orphans: 2; +} + +figcaption, th, td { + widows: 1; + orphans: 1; +} + +h2, h3, h4, h5, h6, dt, +hr, caption { + -webkit-column-break-after: avoid; + page-break-after: avoid; + break-after: avoid; +} + +h1, h2, h3, h4, h5, h6, dt, +figure, tr { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Hyphenation */ + +body { + -webkit-hyphenate-character: "\002D"; + -moz-hyphenate-character: "\002D"; + -ms-hyphenate-character: "\002D"; + hyphenate-character: "\002D"; + -webkit-hyphenate-limit-lines: 3; + -ms-hyphenate-limit-lines: 3; + hyphenate-limit-lines: 3; +} + +h1, h2, h3, h4, h5, h6, dt, +figcaption, pre, caption, address, +center, code, var { + -ms-hyphens: none; + -moz-hyphens: none; + -webkit-hyphens: none; + -epub-hyphens: none; + hyphens: none; +} + +/* OTF */ + +body { + font-variant-numeric: oldstyle-nums proportional-nums; +} + +:lang(ja) body, +:lang(zh) body, +:lang(ko) body { + font-variant-numeric: lining-nums proportional-nums; +} + +h1, h2, h3, h4, h5, h6, dt { + font-variant-numeric: lining-nums proportional-nums; +} + +table { + font-variant-numeric: lining-nums tabular-nums; +} + +code, var { + font-variant-ligatures: none; + font-variant-numeric: lining-nums tabular-nums slashed-zero; +} + +rt { + font-variant-east-asian: ruby; +} + +:lang(ar) { + font-variant-ligatures: common-ligatures; +} + +:lang(ko) { + font-kerning: normal; +} + +/* Night mode */ + +hr { + color: inherit; + border-color: currentColor; +} + +table, th, td { + border-color: currentColor; +} + +/* Horizontal Spacing */ + +figure, blockquote { + margin: 1em 5%; +} + +/* + +:lang(ja) figure, :lang(ja) blockquote, +:lang(zh-Hant) figure, :lang(zh-Hant) blockquote, +:lang(zh-TW) figure, :lang(zh-TW) blockquote, +:lang(mn) figure, :lang(mn) blockquote { + margin: 5% 1em; +} + +*/ + +ul, ol { + padding-left: 5%; +} + +/* + +:lang(ja) ul, :lang(ja) ol, +:lang(zh-Hant) ul, :lang(zh-Hant) ol, +:lang(zh-TW) ul, :lang(zh-TW) ol, +:lang(mn) ul, :lang(mn) ol { + padding-top: 5%; +} + +*/ + +dd { + margin-left: 5%; +} + +/* + +:lang(ja) dd, +:lang(zh-Hant) dd, +:lang(zh-TW) dd, +:lang(mn) dd { + margin-top: 5%; +} + +*/ + +pre { + white-space: pre-wrap; + -ms-tab-size: 2; + -moz-tab-size: 2; + -webkit-tab-size: 2; + tab-size: 2; +} + +/* Normalization */ + +abbr[title], acronym[title] { + text-decoration: dotted underline; +} + +nobr wbr { + white-space: normal; +} + +/* Make ruby text and parentheses non-selectable (TBC) */ + +ruby > rt, ruby > rp { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Internationalization */ + +*:lang(ja), +*:lang(zh), +*:lang(ko), +:lang(ja) cite, :lang(ja) dfn, :lang(ja) em, :lang(ja) i, +:lang(zh) cite, :lang(zh) dfn, :lang(zh) em, :lang(zh) i, +:lang(ko) cite, :lang(ko) dfn, :lang(ko) em, :lang(ko) i { + font-style: normal; +} + +:lang(ja) a, +:lang(zh) a, +:lang(ko) a { + text-decoration: none; +} + +/* Readium CSS + Safeguards module + + A set of styles to prevent common issues in pagination + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* We’ll be using an "RS__" prefix so that we can prevent collisions with authors’ CSS */ + +:root { + /* max-width for media, you can override that via JS if not compiled to static */ + --RS__maxMediaWidth: 100%; + + /* max-height for media, you can override that via JS if not compiled to static + Please consider figures might have a figcaption, which is why 95vh in the first place */ + --RS__maxMediaHeight: 95vh; + + /* value for medias’ box-sizing */ + --RS__boxSizingMedia: border-box; + + /* value for table’s box-sizing */ + --RS__boxSizingTable: border-box; +} + +/* Sanitize line-heights in webkit e.g. raised cap without a declared line-height + See effect by checking this demo in Safari: https://codepen.io/JayPanoz/pen/gRmzrE + Note: glyphs has to be reset to inline for CJK */ + +html { + -webkit-line-box-contain: block glyphs replaced; +} + +:lang(ja) { + -webkit-line-box-contain: block inline replaced; +} + +/* Wrap long strings if larger than line-length */ + +a, h1, h2, h3, h4, h5, h6 { + word-wrap: break-word; +} + +div { + max-width: var(--RS__maxMediaWidth); +} + +/* Size medias */ + +/* You can override CSS variables by re-defining it for all elements or a specific one */ + +img, svg, audio, video { + + /* Object-fit allows us to keep the correct aspect-ratio */ + object-fit: contain; + + width: auto; + height: auto; + + /* Some files don’t have max-width */ + max-width: var(--RS__maxMediaWidth); + + /* We’re setting a max-height, especially for covers */ + max-height: var(--RS__maxMediaHeight) !important; + /* We probably don’t need to use modern box-sizing as auto behaves like it */ + box-sizing: var(--RS__boxSizingMedia); + + /* For page-break, we must use those 3 + We can’t use a variable there, webkit seems to no support them for those properties */ + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Try preventing border being cut-off, webkit + blink have content-box by default */ + +table { + + /* We might want to set at least overflow-x to auto in case the table doesn't fit, + max-width is not necessarily the best solution there, but maybe we could investigate transform scale */ + overflow-x: auto; + box-sizing: var(--RS__boxSizingTable); +} +/*# sourceMappingURL=ReadiumCSS-before.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-default.css b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-default.css new file mode 100644 index 00000000..615a0d17 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/ltr/ReadiumCSS-default.css @@ -0,0 +1,163 @@ +/* Readium CSS + Default module + + A stylesheet for unstyled ebooks based on HTML5 Suggested Rendering + Note: works in combination with Base module + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__compFontFamily: var(--RS__baseFontFamily); + --RS__codeFontFamily: var(--RS__monospaceTf); + + --RS__typeScale: 1.125; /* 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618 */ + --RS__baseFontSize: 100%; + + --RS__flowSpacing: 1.5rem; + --RS__paraSpacing: 0; + --RS__paraIndent: 1em; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + --RS__primaryColor: ; + --RS__secondaryColor: ; +} + +/* STYLES */ + +/* Typo */ + +body { + font-size: var(--RS__baseFontSize); +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--RS__compFontFamily); +} + +/* Flow content */ + +blockquote, figure, p, pre, +aside, footer, form, hr { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +p { + margin-top: var(--RS__paraSpacing); + margin-bottom: var(--RS__paraSpacing); + text-indent: var(--RS__paraIndent); +} + +h1 + p, h2 + p, h3 + p, h4 + p, h5 + p, h6 + p, +hr + p { + text-indent: 0; +} + +pre { + font-family: var(--RS__codeFontFamily); +} + +/* Phrasing content */ + +code, kbd, samp, tt { + font-family: var(--RS__codeFontFamily); +} + +sub, sup { + position: relative; + font-size: 67.5%; + line-height: 1; +} + +sub { + bottom: -0.2ex; +} + +sup { + bottom: 0; +} + +:link { + color: var(--RS__linkColor); +} + +:visited { + color: var(--RS__visitedColor); +} + +/* Headings */ + +h1 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: calc(var(--RS__flowSpacing) * 2); + /* The following is base font size * typescale power of 3 */ + font-size: calc(((1em * var(--RS__typeScale)) * var(--RS__typeScale)) * var(--RS__typeScale)); +} + +h2 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: var(--RS__flowSpacing); + /* The following is base font size * typescale power of 2 */ + font-size: calc((1em * var(--RS__typeScale)) * var(--RS__typeScale)); +} + +h3 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: calc(1em * var(--RS__typeScale)); +} + +h4 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: 1em; +} + +h5 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: 1em; + font-variant: small-caps; +} + +h6 { + margin-top: var(--RS__flowSpacing); + margin-bottom: 0; + font-size: 1em; + text-transform: lowercase; + font-variant: small-caps; +} + +/* Lists */ + +dl, ol, ul { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +/* Table */ + +table { + margin: var(--RS__flowSpacing) 0; + border: 1px solid currentColor; + border-collapse: collapse; + empty-cells: show; +} + +thead, tbody, tfoot, table > tr { + vertical-align: top; +} + +th { + text-align: left; +} + +th, td { + padding: 4px; + border: 1px solid currentColor; +} +/*# sourceMappingURL=ReadiumCSS-default.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-after.css b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-after.css new file mode 100644 index 00000000..4284c835 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-after.css @@ -0,0 +1,760 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Pagination module + + A set of styles to paginate ePublications + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* Columns are responsive by default, even if column-width is set in pixels, + which means two-page spread will switch to single page depending on current font-size. + If you want more control, I’m afraid you’ll have to update colWidth/colGap dynamically, + which is how a significant amount of RS do at the moment. */ + +/* Default for smartphone portrait (small screens) */ + +:root { + /* Your columns’ width floor */ + --RS__colWidth: 45em; /* The width at which we’ll switch to 2 columns by default. PS: you can’t set it in rem */ + + /* Ideal number of columns (depending on columns’ width floor) */ + --RS__colCount: 1; + + /* Gap between columns (in pixels so that it won’t resize with font-size) */ + --RS__colGap: 0; + + /* Optimal line-length (rem will take :root font-size into account, whatever the body’s font-size) */ + --RS__maxLineLength: 40rem; + + /* Default page horizontal margins (in pixels so that it won’t resize with font-size) */ + --RS__pageGutter: 20px; /* See if colGap and pageGutter can be the same var */ +} + +/* Reset page margins for Forward compatibility */ + +@page { + margin: 0 !important; +} + +/* :root selector has same specificity as a class i.e. 0010 + We might have to change that to html / context + -> https://css-tricks.com/almanac/selectors/r/root/ */ + +:root { + + /* In case you use left position to scroll, can be removed if using transform: translateX() */ + position: relative; + + -webkit-column-width: var(--RS__colWidth); + -moz-column-width: var(--RS__colWidth); + column-width: var(--RS__colWidth); + + /* Init pagination */ + /* TODO: document columns’ logic cos it might feel weird at first */ + -webkit-column-count: var(--RS__colCount); + -moz-column-count: var(--RS__colCount); + column-count: var(--RS__colCount); + + -webkit-column-gap: var(--RS__colGap); + -moz-column-gap: var(--RS__colGap); + column-gap: var(--RS__colGap); + + /* Default is balance and we want columns to be filled entirely (100vh) */ + -moz-column-fill: auto; + column-fill: auto; + width: 100%; + height: 100vh; + max-width: 100%; + max-height: 100vh; + min-width: 100%; + min-height: 100vh; + padding: 0 !important; + margin: 0 !important; + + /* Column size will depend on this if we want to make it responsive */ + font-size: 100% !important; + + -webkit-text-size-adjust: 100%; + + /* Switch to newer box model (not inherited by authors’ styles) */ + box-sizing: border-box; + + /* Fix bug for older Chrome */ + -webkit-perspective: 1; + /* Prevents options pop-up when long tap in webkit */ + -webkit-touch-callout: none; +} + +body { + /* overflow: hidden; bugfix: contents won’t paginate in Firefox and one sample in Safari */ + width: 100%; + + /* Limit line-length but we have to reset when 2 columns and control the viewport. + By using max-width + margin auto, margins will shrink when font-size increases, + which is what would be expected in terms of typography. */ + max-width: var(--RS__maxLineLength) !important; + padding: 0 var(--RS__pageGutter) !important; + margin: 0 auto !important; + + /* We need a minimum padding on body so that descandants/ascendants in italic/script are not cut-off. + Drawback: we have to use border-box so that it doesn’t screw the box model, + which means it impacts colWidth and max-width */ + box-sizing: border-box; +} + +/* We’ll now redefine margins and columns depending on the minimum width available + The goal is having the simplest model possible and avoid targeting devices */ + +/* Smartphone landscape */ + +@media screen and (min-width: 35em) { + :root { + --RS__pageGutter: 30px; + } +} + +/* Tablet portrait */ + +@media screen and (min-width: 45em) { + :root { + --RS__pageGutter: 40px; + } +} + +/* Desktop + tablet large */ + +/* We get the previous settings, we just change the margins */ + +@media screen and (min-width: 75em) { + :root { + --RS__pageGutter: 50px; + } +} + +/* At this point (80em or so), constraining line length must be done at the web view/iframe level, or by limiting the size of :root itself */ + +/* Responsive columns */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root { + /* The size at which we want 2 columns to switch to 1 (depending on font-size) */ + --RS__colWidth: 20em; /* 20 * 16 = 320px but 20 * 28 = 560px so it will switch to 1 col @ 175% font-size (user-setting) on an iPad */ + /* We constrain to 2 columns so that we can never get 3 or 4, etc. */ + --RS__colCount: 2; + --RS__maxLineLength: 39.99rem; /* If we don’t use this, colNumber user setting won’t work in Safari… */ + } +} + +/* Readium CSS + Scroll module + + A set of styles to scroll ePublications + This module overrides pagination + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-scroll-on"] { + + /* Reset columns, auto + auto = columns can’t be created */ + -webkit-columns: auto auto !important; + -moz-columns: auto auto !important; + columns: auto auto !important; + width: auto !important; + height: auto !important; + max-width: none !important; + max-height: none !important; + /* Reset html size so that the user can scroll */ + min-width: 0 !important; + min-height: 0 !important; +} + +/* Make sure line-length is limited in all configs */ + +:root[style*="readium-scroll-on"] body { + --RS__maxLineLength: 40rem !important; + + margin-top: var(--RS__pageGutter) !important; + margin-bottom: var(--RS__pageGutter) !important; +} + +/* Scroll mode horizontal */ + +/* Vertical writing needs body height set */ + +/* Do we add a top/bottom margin for body in vertical scroll or not? */ + +/* Readium CSS + Default highlights + + A stylesheet for user highlights + + Repo: https://github.com/readium/readium-css */ + +/* User Highlights */ + +.readiumCSS-yellow-highlight { + background-color: rgba(255, 255, 0, 0.5); +} + +.readiumCSS-green-highlight { + background-color: rgba(0, 255, 0, 0.5); +} + +.readiumCSS-orange-highlight { + background-color: rgba(255, 165, 0, 0.5); +} + +.readiumCSS-pink-highlight { + background-color: rgba(255, 105, 180, 0.5); +} + +/* Media overlays */ + +.readiumCSS-mo-active-default { + color: black !important; + background-color: yellow !important; +} + +/* Readium CSS + Night mode + + A preset theme for night mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +/* [style*="--USER__appearance"] can be used to increase specificity but performance hit */ + +:root[style*="readium-night-on"] { + --RS__backgroundColor: #000000; + --RS__textColor: #FEFEFE; + + --RS__linkColor: #63caff; + --RS__visitedColor: #0099E5; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-night-on"] *:not(a) { + color: inherit !important; + background-color: transparent !important; + border-color: currentColor !important; +} + +:root[style*="readium-night-on"] svg text { + fill: currentColor !important; + stroke: none !important; +} + +:root[style*="readium-night-on"] a:link, +:root[style*="readium-night-on"] a:link * { + color: var(--RS__linkColor) !important; +} + +:root[style*="readium-night-on"] a:visited, +:root[style*="readium-night-on"] a:visited * { + color: var(--RS__visitedColor) !important; +} + +:root[style*="readium-night-on"] img[class*="gaiji"], +:root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); +} + +/* Invert all images on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +/* Darken and invert on user’s demand */ + +:root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%) invert(100%); + filter: brightness(80%) invert(100%); +} + +/* Readium CSS + Sepia mode + + A preset theme for sepia mode + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root[style*="readium-sepia-on"] { + --RS__backgroundColor: #faf4e8; + --RS__textColor: #121212; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; + + --RS__maxLineLength: 40.01rem; /* Forcing a reflow in Blink/Webkit so that blend mode can work */ +} + +/* we don’t need to redeclare bg-color and color for :root since we will inherit and update from day/default mode */ + +:root[style*="readium-sepia-on"] body { + /* Should be transparent but Chrome bug https://bugs.chromium.org/p/chromium/issues/detail?id=711955&q=mix-blend-mode&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified */ + + color: inherit; + background-color: var(--RS__backgroundColor); +} + +:root[style*="readium-sepia-on"] a:link, +:root[style*="readium-sepia-on"] a:link * { + color: var(--RS__linkColor); +} + +:root[style*="readium-sepia-on"] a:visited, +:root[style*="readium-sepia-on"] a:visited * { + color: var(--RS__visitedColor); +} + +:root[style*="readium-sepia-on"] svg, +:root[style*="readium-sepia-on"] img { + /* Make sure the proper bg-color is used for the blend mode */ + background-color: transparent !important; + mix-blend-mode: multiply; +} + +/* Readium CSS + OS Accessibility Modes + + A stylesheet to deal with OS accessibility settings + + Repo: https://github.com/readium/readium-css */ + +/* Windows high contrast colors are mapped to CSS system color keywords + See http://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast */ + +@media screen and (-ms-high-contrast: active) { + :root { + color: windowText !important; + background-color: window !important; + } + + /* The following selectors are super funky but it makes sure everything is inherited, this is indeed critical for this mode */ + :root :not(#\#):not(#\#):not(#\#), + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) + :root :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) :not(#\#):not(#\#):not(#\#) { + color: inherit !important; + background-color: inherit !important; + } + + .readiumCSS-mo-active-default { + color: highlightText !important; + background-color: highlight !important; + } + + /* For links, hyperlink keyword is automatically set */ + + /* Should we also set user highlights? */ +} + +@media screen and (-ms-high-contrast: white-on-black) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +/* Will be true on recent versions of iOS and MacOS if inverted setting enabled by the user */ + +@media screen and (inverted-colors) { + :root[style*="readium-night-on"] img[class*="gaiji"], + :root[style*="readium-night-on"] *[epub\:type~="titlepage"] img:only-child { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-invert-on"] img { + -webkit-filter: none !important; + filter: none !important; + } + :root[style*="readium-night-on"][style*="readium-darken-on"][style*="readium-invert-on"] img { + -webkit-filter: brightness(80%); + filter: brightness(80%); + } +} + +@media screen and (monochrome) { + /* Grayscale (Implemented in Safari, what about eInk?) */ + /* Must deal with anything color (contrast) so must be managed at the night/sepia/theme level :( */ +} + +@media screen and (prefers-reduced-motion) { + /* If reduced motion is set on MacOS, in case we have animation/transition */ +} + +/* Readium CSS + Columns number pref + + A submodule managing columns number for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Number of columns = 1 | 2 */ + +/* We still need to see if we allow users to force number of columns for all configs, currently it behaves as an "auto" setting */ + +/* apply col setting except for mobile portrait */ + +@media screen and (min-width: 60em), screen and (min-device-width: 36em) and (max-device-width: 47em) and (orientation: landscape) { + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"], + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + -webkit-column-count: var(--USER__colCount); + -moz-column-count: var(--USER__colCount); + column-count: var(--USER__colCount); + } + + /* If one column, make sure we limit line-length */ + :root[style*="--USER__colCount: 1"], + :root[style*="--USER__colCount:1"] { + --RS__maxLineLength: 40rem !important; /* This is the only way for the user setting to work in webkit… */ + --RS__colWidth: 100vw; + } + + /* If smartphone landscape, and 2 columns, col width the same as iPad landscape + desktop */ + :root[style*="--USER__colCount: 2"], + :root[style*="--USER__colCount:2"] { + --RS__colWidth: auto; /* User explicitely tells he/she wants 2 columns, we reset floor value */ + } +} + +/* Readium CSS + Page margins pref + + A submodule managing page margins for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +/* Page Margins: the user margin is a factor of the page gutter e.g. 0.5, 0.75, 1, 1.25, 1.5, etc. */ + +:root[style*="--USER__pageMargins"] body { + padding: 0 calc(var(--RS__pageGutter) * var(--USER__pageMargins)) !important; +} + +/* Readium CSS + Custom colors pref + + A submodule managing custom colors for user settings + Part of “Chrome Advanced” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__backgroundColor"] { + background-color: var(--USER__backgroundColor) !important; +} + +:root[style*="--USER__backgroundColor"] * { + background-color: transparent !important; +} + +:root[style*="--USER__textColor"] { + color: var(--USER__textColor) !important; +} + +:root[style*="--USER__textColor"] *:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) { + color: inherit !important; +} + +/* Readium CSS + Text align pref + + A submodule managing text-align for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] { + text-align: var(--USER__textAlign); +} + +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] body, +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] *:not(blockquote):not(figcaption) p, +:root[style*="readium-advanced-on"][style*="--USER__textAlign"] li { + text-align: inherit !important; + -moz-text-align-last: auto !important; + -epub-text-align-last: auto !important; + text-align-last: auto !important; +} + +/* In case something goes wrong at the programmatic level + rtl for body + rtl in ltr */ + +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign: left"], +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign:left"], +:root[style*="readium-advanced-on"][style*="--USER__textAlign: left"] *[dir="rtl"], +:root[style*="readium-advanced-on"][style*="--USER__textAlign:left"] *[dir="rtl"] { + text-align: right; +} + +/* Edge, if logical value is used, think of it as a polyfill. For LTR, it will fall back to the default, which is left */ + +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign: start"], +:root[style*="readium-advanced-on"][dir="rtl"][style*="--USER__textAlign:start"] { + text-align: right; +} + +/* Readium CSS + Font Family pref + + A submodule managing font-family for user settings + Part of “User Overrides” class – “font override” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] { + font-family: var(--USER__fontFamily) !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] body, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] p, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] li, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] div, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dt, +:root[style*="readium-font-on"][style*="--USER__fontFamily"] dd { + font-family: inherit !important; +} + +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] i:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] em:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] cite:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] b:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] strong:not([xml\:lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([lang]), +:root[style*="readium-font-on"][style*="--USER__fontFamily"] span:not([xml\:lang]) { + font-family: inherit !important; +} + +/* Readium CSS + Font size pref + + A submodule managing font-size for user settings + Part of “User Overrides” class – no flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="--USER__fontSize"] { + font-size: var(--USER__fontSize) !important; +} + +/* Readium CSS + Line height pref + + A submodule managing line-height for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] { + line-height: var(--USER__lineHeight) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] body, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] p, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] li, +:root[style*="readium-advanced-on"][style*="--USER__lineHeight"] div { + line-height: inherit; +} + +/* Readium CSS + Para spacing pref + + A submodule managing paragraphs’ top and bottom margins for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraSpacing"] p { + margin-top: var(--USER__paraSpacing) !important; + margin-bottom: var(--USER__paraSpacing) !important; +} + +/* Readium CSS + Para indent pref + + A submodule managing paragraphs’ text-indent for user settings + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p { + text-indent: var(--USER__paraIndent) !important; +} + +/* If there are inline-block elements in paragraphs, text-indent will inherit so we must reset it */ + +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p *, +:root[style*="readium-advanced-on"][style*="--USER__paraIndent"] p:first-letter { + text-indent: 0 !important; +} + +/* Readium CSS + Ligatures pref + + A submodule managing ligatures for user settings in the Arabic Script (can help with a11y) + Part of “User Overrides Advanced” class – “advanced settings” flag required. + + Repo: https://github.com/readium/readium-css */ + +:root[style*="readium-advanced-on"][style*="--USER__arabicLigatures"] { + font-variant-ligatures: var(--USER__ligatures) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__arabicLigatures"] * { + font-variant-ligatures: inherit !important; +} + +/* Readium CSS + Font size normalize + + A stylesheet to normalize font-size + + Repo: https://github.com/readium/readium-css */ + +/* STYLES */ + +/* :root is used so that you can quickly add a class or attribute if you prefer e.g. `:root[data-rs-normalize]` */ + +/* We create a default so that you don’t need to explicitly set one in the DOM. + Once the “Publisher’s styles” checkbox is unchecked, the normalize is applied automatically */ + +:root[style*="readium-advanced-on"] { + --USER__typeScale: 1.2; /* This is the default type scale you’ll find in most publications */ +} + +:root[style*="readium-advanced-on"] p, +:root[style*="readium-advanced-on"] li, +:root[style*="readium-advanced-on"] div, +:root[style*="readium-advanced-on"] pre, +:root[style*="readium-advanced-on"] dd { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] h1 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.75rem !important; + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h2 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.5rem !important; + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h3 { + /* Fallback if browser doesn’t support vars */ + font-size: 1.25rem !important; + font-size: calc(1rem * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"] h4, +:root[style*="readium-advanced-on"] h5, +:root[style*="readium-advanced-on"] h6 { + font-size: 1rem !important; +} + +:root[style*="readium-advanced-on"] small { + font-size: smaller !important; +} + +:root[style*="readium-advanced-on"] sub, +:root[style*="readium-advanced-on"] sup { + font-size: 67.5% !important; +} + +/* The following styles kick in if you define the typeScale variable in the DOM. + No need to repeat declarations which don’t make use of the variable */ + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h1 { + font-size: calc(((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h2 { + font-size: calc((1rem * var(--USER__typeScale)) * var(--USER__typeScale)) !important; +} + +:root[style*="readium-advanced-on"][style*="--USER__typeScale"] h3 { + font-size: calc(1rem * var(--USER__typeScale)) !important; +} +/*# sourceMappingURL=ReadiumCSS-after.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-before.css b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-before.css new file mode 100644 index 00000000..28778589 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-before.css @@ -0,0 +1,575 @@ +/* Readium CSS + Config module + + A file allowing implementers to customize flags for reading modes, + user settings, etc. + + Repo: https://github.com/readium/readium-css */ + +/* Custom medias + Syntax: @custom-media --variable (prop: value) */ + +/* Responsive columns + The minimum width for which responsive columns (2 -> 1 and vice versa, + depending on the current font-size) must be enabled */ + +/* Mobile columns + The minimum and maximum width for mobile devices. + We’re forcing the landscape orientation by default, + and must still investigate large tablets (iPad Pro, Surface Pro 3, etc.). */ + +/* Custom selectors + Syntax: @custom-selector :--variable selector + The selectors you will use for flags/switches + You can alternatively use classes or custom data-* attributes */ + +/* User view = paged | scrolled */ + +/* Font-family override */ + +/* Advanced settings */ + +/* Reading Modes */ + +/* Filters (images) */ + +/* Accessibility normalization */ + +/* Accessibility font. You can add selectors, using “, ” as a separator, if you have multiple fonts */ + +/* Direction i.e. ltr and rtl */ + +/* Readium CSS + Base module + + A minimal stylesheet for all ebooks + + Repo: https://github.com/readium/readium-css */ + +@namespace url("http://www.w3.org/1999/xhtml"); + +@namespace epub url("http://www.idpf.org/2007/ops"); + +@namespace m url("http://www.w3.org/1998/Math/MathML/"); + +@namespace svg url("http://www.w3.org/2000/svg"); + +/* Define viewport, HTML5-style */ + +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; + zoom: 1; +} + +:root { + /* Default font-stacks */ + --RS__oldStyleTf: "Iowan Old Style", "Sitka Text", Palatino, "Book Antiqua", serif; + --RS__modernTf: Athelas, Constantia, Georgia, serif; + --RS__sansTf: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --RS__humanistTf: Seravek, Calibri, Roboto, Arial, sans-serif; + --RS__monospaceTf: "Andale Mono", Consolas, monospace; + + /* Config */ + --RS__baseFontFamily: var(--RS__oldStyleTf); + + /* For square-ish fonts (CJK, Indic, etc.), we must apply some compensation in dynamic leading. Default is 1 i.e. no compensation */ + --RS__lineHeightCompensation: 1; + + /* Dynamic leading based on typeface metrics + font-size setting */ + --RS__baseLineHeight: calc((1em + (2ex - 1ch) - ((1rem - 16px) * 0.1667)) * var(--RS__lineHeightCompensation)); +} + +/* Set default font for the html doc, so that it can be overridden by the authors’s stylesheet */ + +html { + font-family: var(--RS__baseFontFamily); + /* Fallback line-height */ + line-height: 1.6; /* Fits a little bit better for all languages than 1.5 */ + line-height: var(--RS__baseLineHeight); + text-rendering: optimizeLegibility; +} + +/* 1.5 being too loose with larger font-sizes, we reset headings to normal (which value is 1.125–1.375 for our font-stacks) */ + +h1, h2, h3 { + line-height: normal; +} + +:lang(ja), +:lang(zh), +:lang(ko) { + word-wrap: break-word; + -webkit-line-break: strict; + -epub-line-break: strict; + line-break: strict; +} + +/* Set default font for Math */ + +math { + font-family: "Latin Modern Math", "STIX Two Math", "XITS Math", "STIX Math", "Libertinus Math", "TeX Gyre Termes Math", "TeX Gyre Bonum Math", "TeX Gyre Schola", "DejaVu Math TeX Gyre", "TeX Gyre Pagella Math", "Asana Math", "Cambria Math", "Lucida Bright Math", "Minion Math", STIXGeneral, STIXSizeOneSym, Symbol, "Times New Roman", serif; +} + +/* Language Overrides + That will only work if either html or body have a (xml:)lang attribute, not for inline overrides */ + +:lang(am) { + --RS__baseFontFamily: Kefa, Nyala, Roboto, Noto, "Noto Sans Ethiopic", serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ar) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(bn) { + --RS__baseFontFamily: "Kohinoor Bangla", "Bangla Sangam MN", Vrinda, Roboto, Noto, "Noto Sans Bengali", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(bo) { + --RS__baseFontFamily: Kailasa, "Microsoft Himalaya", Roboto, Noto, "Noto Sans Tibetan", sans-serif; +} + +:lang(chr) { + --RS__baseFontFamily: "Plantagenet Cherokee", Roboto, Noto, "Noto Sans Cherokee"; + --RS__lineHeightCompensation: 1.167; +} + +:lang(fa) { + --RS__baseFontFamily: "Geeza Pro", "Arabic Typesetting", Roboto, Noto, "Noto Naskh Arabic", "Times New Roman", serif; +} + +:lang(gu) { + --RS__baseFontFamily: "Gujarati Sangam MN", "Nirmala UI", Shruti, Roboto, Noto, "Noto Sans Gujarati", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(he) { + --RS__baseFontFamily: "New Peninim MT", "Arial Hebrew", Gisha, "Times New Roman", Roboto, Noto, "Noto Sans Hebrew" sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(hi) { + --RS__baseFontFamily: "Kohinoor Devanagari", "Devanagari Sangam MN", Kokila, "Nirmala UI", Roboto, Noto, "Noto Sans Devanagari", sans-serif; + + --RS__lineHeightCompensation: 1.1; +} + +:lang(hy) { + --RS__baseFontFamily: Mshtakan, Sylfaen, Roboto, Noto, "Noto Serif Armenian", serif; +} + +:lang(iu) { + --RS__baseFontFamily: "Euphemia UCAS", Euphemia, Roboto, Noto, "Noto Sans Canadian Aboriginal", sans-serif; +} + +:lang(ja) { + --RS__baseFontFamily: "游ゴシック体", YuGothic, "ヒラギノ丸ゴ", "Hiragino Sans", "Yu Gothic UI", "Meiryo UI", "MS Gothic", Roboto, Noto, "Noto Sans CJK JP", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; + + /* Extra variables for Japanese font-stacks as we may want to reuse them for user settings + default */ + --RS__serif-ja: "MS P明朝", "MS PMincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS 明朝", "MS Mincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja: "MS Pゴシック", "MS PGothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS ゴシック", "MS Gothic", "Hiragino Sans", sans-serif; + --RS__serif-ja-v: "MS 明朝", "MS Mincho", "Hiragino Mincho Pro", "ヒラギノ明朝 Pro W3", "游明朝", "YuMincho", "MS P明朝", "MS PMincho", "Hiragino Mincho ProN", serif; + --RS__sans-serif-ja-v: "MS ゴシック", "MS Gothic", "Hiragino Kaku Gothic Pro W3", "ヒラギノ角ゴ Pro W3", "Hiragino Sans GB", "ヒラギノ角ゴシック W3", "游ゴシック", "YuGothic", "MS Pゴシック", "MS PGothic", "Hiragino Sans", sans-serif; +} + +:lang(km) { + --RS__baseFontFamily: "Khmer Sangam MN", "Leelawadee UI", "Khmer UI", Roboto, Noto, "Noto Sans Khmer", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(kn) { + --RS__baseFontFamily: "Kannada Sangam MN", "Nirmala UI", Tunga, Roboto, Noto, "Noto Sans Kannada", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(ko) { + --RS__baseFontFamily: "Nanum Gothic", "Apple SD Gothic Neo", "Malgun Gothic", Roboto, Noto, "Noto Sans CJK KR", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(lo) { + --RS__baseFontFamily: "Lao Sangam MN", "Leelawadee UI", "Lao UI", Roboto, Noto, "Noto Sans Lao", sans-serif; +} + +:lang(ml) { + --RS__baseFontFamily: "Malayalam Sangam MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Malayalam", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(or) { + --RS__baseFontFamily: "Oriya Sangam MN", "Nirmala UI", Kalinga, Roboto, Noto, "Noto Sans Oriya", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(pa) { + --RS__baseFontFamily: "Gurmukhi MN", "Nirmala UI", Kartika, Roboto, Noto, "Noto Sans Gurmukhi", sans-serif; + --RS__lineHeightCompensation: 1.1; +} + +:lang(si) { + --RS__baseFontFamily: "Sinhala Sangam MN", "Nirmala UI", "Iskoola Pota", Roboto, Noto, "Noto Sans Sinhala", sans-serif; + --RS__lineHeightCompensation: 1.167; +} + +:lang(ta) { + --RS__baseFontFamily: "Tamil Sangam MN", "Nirmala UI", Latha, Roboto, Noto, "Noto Sans Tamil", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +:lang(te) { + --RS__baseFontFamily: "Kohinoor Telugu", "Telugu Sangam MN", "Nirmala UI", Gautami, Roboto, Noto, "Noto Sans Telugu", sans-serif; +} + +:lang(th) { + --RS__baseFontFamily: "Thonburi", "Leelawadee UI", "Cordia New", Roboto, Noto, "Noto Sans Thai", sans-serif; + --RS__lineHeightCompensation: 1.067; +} + +/* The following will also work for zh-Hans */ + +:lang(zh) { + --RS__baseFontFamily: "方体", "PingFang SC", "黑体", "Heiti SC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK SC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-Hant), +:lang(zh-TW) { + --RS__baseFontFamily: "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +:lang(zh-HK) { + --RS__baseFontFamily: "方體", "PingFang HK", "方體", "PingFang TC", "黑體", "Heiti TC", "Microsoft JhengHei UI", "Microsoft JhengHei", Roboto, Noto, "Noto Sans CJK TC", sans-serif; + + /* For CJK, the line-height is usually 15–20% more than for Latin */ + --RS__lineHeightCompensation: 1.167; +} + +/* Readium CSS + Day/Default mode + + A preset theme for day mode, which is the default + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__backgroundColor: #FFFFFF; + --RS__textColor: #121212; + + /* This can be customized but initial will re-use default value of the browser */ + --RS__selectionBackgroundColor: #b4d8fe; + --RS__selectionTextColor: inherit; +} + +:root { + color: var(--RS__textColor) !important; + background-color: var(--RS__backgroundColor) !important; +} + +/* Note: Though `::selection` was present in drafts of CSS Selectors Level 3, it was removed during the Candidate Recommendation phase because its behavior was under-specified (especially with nested elements) and interoperability wasn’t achieved. Source: https://developer.mozilla.org/en-US/docs/Web/CSS/::selection */ + +::-moz-selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +::selection { + color: var(--RS__selectionTextColor); + background-color: var(--RS__selectionBackgroundColor); +} + +/* @import "modules/ReadiumCSS-fonts.css"; */ + +/* Readium CSS + HTML5 SR Patch stylesheet + + A set of style to adjust HTML5 Suggested Rendering to paginated content + + Repo: https://github.com/readium/readium-css */ + +/* Fragmentation */ + +body { + widows: 2; + orphans: 2; +} + +figcaption, th, td { + widows: 1; + orphans: 1; +} + +h2, h3, h4, h5, h6, dt, +hr, caption { + -webkit-column-break-after: avoid; + page-break-after: avoid; + break-after: avoid; +} + +h1, h2, h3, h4, h5, h6, dt, +figure, tr { + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Hyphenation */ + +body { + -webkit-hyphenate-character: "\002D"; + -moz-hyphenate-character: "\002D"; + -ms-hyphenate-character: "\002D"; + hyphenate-character: "\002D"; + -webkit-hyphenate-limit-lines: 3; + -ms-hyphenate-limit-lines: 3; + hyphenate-limit-lines: 3; +} + +h1, h2, h3, h4, h5, h6, dt, +figcaption, pre, caption, address, +center, code, var { + -ms-hyphens: none; + -moz-hyphens: none; + -webkit-hyphens: none; + -epub-hyphens: none; + hyphens: none; +} + +/* OTF */ + +body { + font-variant-numeric: oldstyle-nums proportional-nums; +} + +:lang(ja) body, +:lang(zh) body, +:lang(ko) body { + font-variant-numeric: lining-nums proportional-nums; +} + +h1, h2, h3, h4, h5, h6, dt { + font-variant-numeric: lining-nums proportional-nums; +} + +table { + font-variant-numeric: lining-nums tabular-nums; +} + +code, var { + font-variant-ligatures: none; + font-variant-numeric: lining-nums tabular-nums slashed-zero; +} + +rt { + font-variant-east-asian: ruby; +} + +:lang(ar) { + font-variant-ligatures: common-ligatures; +} + +:lang(ko) { + font-kerning: normal; +} + +/* Night mode */ + +hr { + color: inherit; + border-color: currentColor; +} + +table, th, td { + border-color: currentColor; +} + +/* Horizontal Spacing */ + +figure, blockquote { + margin: 1em 5%; +} + +/* + +:lang(ja) figure, :lang(ja) blockquote, +:lang(zh-Hant) figure, :lang(zh-Hant) blockquote, +:lang(zh-TW) figure, :lang(zh-TW) blockquote, +:lang(mn) figure, :lang(mn) blockquote { + margin: 5% 1em; +} + +*/ + +ul, ol { + padding-left: 5%; +} + +/* + +:lang(ja) ul, :lang(ja) ol, +:lang(zh-Hant) ul, :lang(zh-Hant) ol, +:lang(zh-TW) ul, :lang(zh-TW) ol, +:lang(mn) ul, :lang(mn) ol { + padding-top: 5%; +} + +*/ + +dd { + margin-left: 5%; +} + +/* + +:lang(ja) dd, +:lang(zh-Hant) dd, +:lang(zh-TW) dd, +:lang(mn) dd { + margin-top: 5%; +} + +*/ + +pre { + white-space: pre-wrap; + -ms-tab-size: 2; + -moz-tab-size: 2; + -webkit-tab-size: 2; + tab-size: 2; +} + +/* Normalization */ + +abbr[title], acronym[title] { + text-decoration: dotted underline; +} + +nobr wbr { + white-space: normal; +} + +/* Make ruby text and parentheses non-selectable (TBC) */ + +ruby > rt, ruby > rp { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Internationalization */ + +*:lang(ja), +*:lang(zh), +*:lang(ko), +:lang(ja) cite, :lang(ja) dfn, :lang(ja) em, :lang(ja) i, +:lang(zh) cite, :lang(zh) dfn, :lang(zh) em, :lang(zh) i, +:lang(ko) cite, :lang(ko) dfn, :lang(ko) em, :lang(ko) i { + font-style: normal; +} + +:lang(ja) a, +:lang(zh) a, +:lang(ko) a { + text-decoration: none; +} + +/* Readium CSS + Safeguards module + + A set of styles to prevent common issues in pagination + + Repo: https://github.com/readium/readium-css */ + +/* Config */ + +/* We’ll be using an "RS__" prefix so that we can prevent collisions with authors’ CSS */ + +:root { + /* max-width for media, you can override that via JS if not compiled to static */ + --RS__maxMediaWidth: 100%; + + /* max-height for media, you can override that via JS if not compiled to static + Please consider figures might have a figcaption, which is why 95vh in the first place */ + --RS__maxMediaHeight: 95vh; + + /* value for medias’ box-sizing */ + --RS__boxSizingMedia: border-box; + + /* value for table’s box-sizing */ + --RS__boxSizingTable: border-box; +} + +/* Sanitize line-heights in webkit e.g. raised cap without a declared line-height + See effect by checking this demo in Safari: https://codepen.io/JayPanoz/pen/gRmzrE + Note: glyphs has to be reset to inline for CJK */ + +html { + -webkit-line-box-contain: block glyphs replaced; +} + +:lang(ja) { + -webkit-line-box-contain: block inline replaced; +} + +/* Wrap long strings if larger than line-length */ + +a, h1, h2, h3, h4, h5, h6 { + word-wrap: break-word; +} + +div { + max-width: var(--RS__maxMediaWidth); +} + +/* Size medias */ + +/* You can override CSS variables by re-defining it for all elements or a specific one */ + +img, svg, audio, video { + + /* Object-fit allows us to keep the correct aspect-ratio */ + object-fit: contain; + + width: auto; + height: auto; + + /* Some files don’t have max-width */ + max-width: var(--RS__maxMediaWidth); + + /* We’re setting a max-height, especially for covers */ + max-height: var(--RS__maxMediaHeight) !important; + /* We probably don’t need to use modern box-sizing as auto behaves like it */ + box-sizing: var(--RS__boxSizingMedia); + + /* For page-break, we must use those 3 + We can’t use a variable there, webkit seems to no support them for those properties */ + -webkit-column-break-inside: avoid; + page-break-inside: avoid; + break-inside: avoid; +} + +/* Try preventing border being cut-off, webkit + blink have content-box by default */ + +table { + max-width: var(--RS__maxMediaWidth); + box-sizing: var(--RS__boxSizingTable); +} +/*# sourceMappingURL=ReadiumCSS-before.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-default.css b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-default.css new file mode 100644 index 00000000..83a5dd84 --- /dev/null +++ b/r2-navigator/src/main/assets/static/styles/rtl/ReadiumCSS-default.css @@ -0,0 +1,163 @@ +/* Readium CSS + Default module for RTL scripts + + A stylesheet for unstyled ebooks based on HTML5 Suggested Rendering + Note: works in combination with Base module + + Repo: https://github.com/readium/readium-css */ + +/* CONFIG */ + +:root { + --RS__compFontFamily: var(--RS__baseFontFamily); + --RS__codeFontFamily: var(--RS__monospaceTf); + + --RS__typeScale: 1.125; /* 1.067 | 1.125 | 1.2 | 1.25 | 1.333 | 1.414 | 1.5 | 1.618 */ + --RS__baseFontSize: 100%; + + --RS__flowSpacing: 1.5rem; + --RS__paraSpacing: 0; + --RS__paraIndent: 1em; + + --RS__linkColor: #0000EE; + --RS__visitedColor: #551A8B; + + --RS__primaryColor: ; + --RS__secondaryColor: ; +} + +/* STYLES */ + +/* Typo */ + +body { + font-size: var(--RS__baseFontSize); + text-align: justify; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--RS__compFontFamily); + text-align: right; +} + +/* Flow content */ + +blockquote, figure, p, pre, +aside, footer, form, hr { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +p { + margin-top: var(--RS__paraSpacing); + margin-bottom: var(--RS__paraSpacing); + text-indent: var(--RS__paraIndent); +} + +h1 + p, h2 + p, h3 + p, h4 + p, h5 + p, h6 + p, +hr + p { + text-indent: 0; +} + +pre { + font-family: var(--RS__codeFontFamily); +} + +/* Phrasing content */ + +code, kbd, samp, tt { + font-family: var(--RS__codeFontFamily); +} + +sub, sup { + position: relative; + font-size: 67.5%; + line-height: 1; +} + +sub { + bottom: -0.2ex; +} + +sup { + bottom: 0; +} + +:link { + color: var(--RS__linkColor); +} + +:visited { + color: var(--RS__visitedColor); +} + +/* Headings */ + +h1 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: calc(var(--RS__flowSpacing) * 2); + /* The following is base font size * typescale power of 3 */ + font-size: calc(((1em * var(--RS__typeScale)) * var(--RS__typeScale)) * var(--RS__typeScale)); +} + +h2 { + margin-top: calc(var(--RS__flowSpacing) * 2); + margin-bottom: var(--RS__flowSpacing); + /* The following is base font size * typescale power of 2 */ + font-size: calc((1em * var(--RS__typeScale)) * var(--RS__typeScale)); +} + +h3 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: calc(1em * var(--RS__typeScale)); +} + +h4 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: 1em; +} + +h5 { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); + font-size: smaller; +} + +h6 { + margin-top: var(--RS__flowSpacing); + margin-bottom: 0; + font-size: smaller; + font-weight: normal; +} + +/* Lists */ + +dl, ol, ul { + margin-top: var(--RS__flowSpacing); + margin-bottom: var(--RS__flowSpacing); +} + +/* Table */ + +table { + margin: var(--RS__flowSpacing) 0; + border: 1px solid currentColor; + border-collapse: collapse; + empty-cells: show; +} + +thead, tbody, tfoot, table > tr { + vertical-align: top; +} + +th { + text-align: initial; +} + +th, td { + padding: 4px; + border: 1px solid currentColor; +} +/*# sourceMappingURL=ReadiumCSS-default.css.map */ \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/Globals.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/GlobalVars.kt similarity index 56% rename from r2-navigator/src/main/java/org/readium/r2/navigator/Globals.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/GlobalVars.kt index 4b98253d..211043b1 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/Globals.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/GlobalVars.kt @@ -9,11 +9,6 @@ package org.readium.r2.navigator -import android.content.Context -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.core.content.ContextCompat - /** * Created by aferditamuriqi on 10/3/17. */ @@ -22,17 +17,5 @@ import androidx.core.content.ContextCompat /** * Global Parameters */ -//val PORT_NUMBER = 3333 const val BASE_URL = "http://127.0.0.1" -//val SERVER_URL = "$BASE_URL:$PORT_NUMBER" -//val MANIFEST = "/manifest" - - -/** - * Extensions - */ -@ColorInt -fun Context.color(@ColorRes id: Int): Int { - return ContextCompat.getColor(this, id) -} \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/IR2Activity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/IR2Activity.kt new file mode 100644 index 00000000..06f4707b --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/IR2Activity.kt @@ -0,0 +1,147 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Aferdita Muriqi + * + * Copyright (c) 2019. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator + +import android.content.SharedPreferences +import android.view.View +import org.readium.r2.navigator.pager.R2ViewPager +import org.readium.r2.shared.Link +import org.readium.r2.shared.Locator +import org.readium.r2.shared.Publication +import java.net.URL + +interface IR2Activity { + + val publication: Publication + val preferences: SharedPreferences + val publicationIdentifier: String + val publicationFileName: String + val publicationPath: String + val bookId: Long + val resourcePager: R2ViewPager? + get() = null + val allowToggleActionBar: Boolean + get() = true + + fun toggleActionBar() {} + fun toggleActionBar(v: View? = null) {} + fun nextResource(v: View? = null) {} + fun previousResource(v: View? = null) {} + fun onPageChanged(pageIndex: Int, totalPages: Int, url: String) {} + fun onPageEnded(end: Boolean) {} + fun onPageLoaded() {} + fun highlightActivated(id: String) {} + fun highlightAnnotationMarkActivated(id: String) {} + fun progressionDidChange(progression: Double) {} +} + +interface IR2TTS { + fun playTextChanged(text: String) {} + fun playStateChanged(playing: Boolean) {} + fun dismissScreenReader() {} +} + + +interface Navigator { + val currentLocation: Locator? + get() = null + + fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean + fun go(link: Link, animated: Boolean, completion: () -> Unit): Boolean + fun goForward(animated: Boolean, completion: () -> Unit): Boolean + fun goBackward(animated: Boolean, completion: () -> Unit): Boolean + +} + +fun Navigator.go(locator: Locator, animated: Boolean = false, completion: () -> Unit = {}): Boolean = + go(locator = locator, animated = animated, completion = completion) + +fun Navigator.go(link: Link, animated: Boolean = false, completion: () -> Unit = {}): Boolean = + go(link = link, animated = animated, completion = completion) + +fun Navigator.goForward(animated: Boolean = false, completion: () -> Unit = {}): Boolean = + goForward(animated = animated, completion = completion) + +fun Navigator.goBackward(animated: Boolean = false, completion: () -> Unit = {}): Boolean = + goBackward(animated = animated, completion = completion) + + +interface NavigatorDelegate { + fun locationDidChange(navigator: Navigator? = null, locator: Locator) + + // present error message + fun presentError(navigator: Navigator? = null, error: NavigatorError) {} + + // present external url + fun presentExternalURL(navigator: Navigator? = null, url: URL) {} +} + + +//public fun NavigatorDelegate.navigator(navigator: Navigator, url: URL) { +// if (UIApplication.shared.canOpenURL(url)) { +// UIApplication.shared.openURL(url) +// } +//} + + +sealed class NavigatorError : Exception() { + object copyForbidden : NavigatorError() + + val errorDescription: String? + get() { + return when (this) { + is copyForbidden -> "NavigatorError.copyForbidden" + } + } +} + + +enum class ReadingProgression(val rawValue: String) { + rtl("rtl"), ltr("ltr"), auto("auto"); + + companion object { + operator fun invoke(rawValue: String) = ReadingProgression.values().firstOrNull { it.rawValue == rawValue } + } +} + + +interface VisualNavigator : Navigator { + // val view: UIView + val readingProgression: ReadingProgression + + fun goLeft(animated: Boolean, completion: () -> Unit): Boolean + fun goRight(animated: Boolean, completion: () -> Unit): Boolean +} + + +fun VisualNavigator.goLeft(animated: Boolean = false, completion: () -> Unit = {}): Boolean { + return when (readingProgression) { + ReadingProgression.ltr -> goBackward(animated = animated, completion = completion) + ReadingProgression.auto -> goBackward(animated = animated, completion = completion) + ReadingProgression.rtl -> goForward(animated = animated, completion = completion) + } +} + +fun VisualNavigator.goRight(animated: Boolean = false, completion: () -> Unit = {}): Boolean { + return when (readingProgression) { + ReadingProgression.ltr -> goForward(animated = animated, completion = completion) + ReadingProgression.auto -> goForward(animated = animated, completion = completion) + ReadingProgression.rtl -> goBackward(animated = animated, completion = completion) + } +} + + +//public interface VisualNavigatorDelegate: NavigatorDelegate { +// fun navigator(navigator: VisualNavigator, point: CGPoint) +//} + +//public fun VisualNavigatorDelegate.navigator(navigator: VisualNavigator, point: CGPoint) {} + + diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2BasicWebView.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt similarity index 67% rename from r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2BasicWebView.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index d5da539a..8f2ee975 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2BasicWebView.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -7,7 +7,7 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.pager +package org.readium.r2.navigator import android.content.Context import android.os.Build @@ -21,15 +21,12 @@ import android.widget.ImageButton import android.widget.ListPopupWindow import android.widget.PopupWindow import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.jsoup.Jsoup import org.jsoup.safety.Whitelist -import org.readium.r2.navigator.BuildConfig -import org.readium.r2.navigator.R -import org.readium.r2.navigator.R2EpubActivity -import org.readium.r2.shared.Locations import org.readium.r2.shared.SCROLL_REF import org.readium.r2.shared.getAbsolute @@ -40,7 +37,10 @@ import org.readium.r2.shared.getAbsolute open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(context, attrs) { - lateinit var activity: R2EpubActivity + lateinit var activity: AppCompatActivity + lateinit var listener: IR2Activity + lateinit var navigator: Navigator + var progression: Double = 0.0 var overrideUrlLoading = true var resourceUrl: String? = null @@ -50,7 +50,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte private val uiScope = CoroutineScope(Dispatchers.Main) init { - setWebContentsDebuggingEnabled(BuildConfig.DEBUG) + setWebContentsDebuggingEnabled(BuildConfig.DEBUG) } interface OnOverScrolledCallback { @@ -70,30 +70,30 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte @android.webkit.JavascriptInterface - open fun scrollRight() { + open fun scrollRight(animated: Boolean = false) { uiScope.launch { - if (activity.supportActionBar!!.isShowing && activity.allowToggleActionBar) { - activity.resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + if (activity.supportActionBar!!.isShowing && listener.allowToggleActionBar) { + listener.resourcePager?.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar or View.SYSTEM_UI_FLAG_IMMERSIVE) } - val scrollMode = activity.preferences.getBoolean(SCROLL_REF, false) + val scrollMode = listener.preferences.getBoolean(SCROLL_REF, false) if (scrollMode) { - if (activity.publication.metadata.direction == "rtl") { + if (listener.publication.metadata.direction == "rtl") { this@R2BasicWebView.evaluateJavascript("scrollRightRTL();") { result -> if (result.contains("edge")) { - activity.previousResource(false) + navigator.goBackward(animated = animated) } } } else { - activity.nextResource(false) + navigator.goForward(animated = animated) } } else { if (!this@R2BasicWebView.canScrollHorizontally(1)) { - activity.nextResource(false) + navigator.goForward(animated = animated) } this@R2BasicWebView.evaluateJavascript("scrollRight();", null) } @@ -101,31 +101,31 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte } @android.webkit.JavascriptInterface - open fun scrollLeft() { + open fun scrollLeft(animated: Boolean = false) { uiScope.launch { - if (activity.supportActionBar!!.isShowing && activity.allowToggleActionBar) { - activity.resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + if (activity.supportActionBar!!.isShowing && listener.allowToggleActionBar) { + listener.resourcePager?.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar or View.SYSTEM_UI_FLAG_IMMERSIVE) } - val scrollMode = activity.preferences.getBoolean(SCROLL_REF, false) + val scrollMode = listener.preferences.getBoolean(SCROLL_REF, false) if (scrollMode) { - if (activity.publication.metadata.direction == "rtl") { + if (listener.publication.metadata.direction == "rtl") { this@R2BasicWebView.evaluateJavascript("scrollLeftRTL();") { result -> if (result.contains("edge")) { - activity.nextResource(false) + navigator.goForward(animated = animated) } } } else { - activity.previousResource(false) + navigator.goBackward(animated = animated) } } else { // fix this for when vertical scrolling is enabled if (!this@R2BasicWebView.canScrollHorizontally(-1)) { - activity.previousResource(false) + navigator.goBackward(animated = animated) } this@R2BasicWebView.evaluateJavascript("scrollLeft();", null) } @@ -135,12 +135,12 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte @android.webkit.JavascriptInterface fun progressionDidChange(positionString: String) { progression = positionString.toDouble() - activity.storeProgression(Locations(progression = progression)) + listener.progressionDidChange(progression) } @android.webkit.JavascriptInterface fun centerTapped() { - activity.toggleActionBar() + listener.toggleActionBar() } @android.webkit.JavascriptInterface @@ -150,7 +150,7 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte link?.let { noteref -> val href = noteref.attr("href") if (href.indexOf("#") > 0) { - val id = href.substring(href.indexOf('#')+1) + val id = href.substring(href.indexOf('#') + 1) var absolute = getAbsolute(href, resourceUrl!!) absolute = absolute.substring(0, absolute.indexOf("#")) val document = Jsoup.connect(absolute).get() @@ -204,6 +204,21 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte } } + @android.webkit.JavascriptInterface + fun highlightActivated(id: String) { + uiScope.launch { + listener.highlightActivated(id) + } + } + + @android.webkit.JavascriptInterface + fun highlightAnnotationMarkActivated(id: String) { + uiScope.launch { + listener.highlightAnnotationMarkActivated(id) + } + } + + fun Boolean.toInt() = if (this) 1 else 0 fun scrollToStart() { @@ -215,14 +230,67 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte } fun scrollToPosition(progression: Double) { - this.evaluateJavascript("scrollToPosition(\"$progression\", \"${activity.publication.metadata.direction}\");", null) + this.evaluateJavascript("scrollToPosition(\"$progression\", \"${listener.publication.metadata.direction}\");", null) + } + + fun setScrollMode(scrollMode: Boolean) { + this.evaluateJavascript("setScrollMode($scrollMode)", null) } fun setProperty(key: String, value: String) { - this.evaluateJavascript("setProperty(\"$key\", \"$value\");", null) + this.evaluateJavascript("setProperty(\"$key\", \"$value\");") { + listener.onPageLoaded() + } } fun removeProperty(key: String) { this.evaluateJavascript("removeProperty(\"$key\");", null) } + + fun getCurrentSelectionInfo(callback: (String) -> Unit) { + this.evaluateJavascript("getCurrentSelectionInfo();") { + callback(it) + } + } + + fun getCurrentSelectionRect(callback: (String) -> Unit) { + this.evaluateJavascript("getSelectionRect();") { + callback(it) + } + } + + fun createHighlight(locator: String?, color: String?, callback: (String) -> Unit) { + uiScope.launch { + this@R2BasicWebView.evaluateJavascript("createHighlight($locator, $color, true);") { + callback(it) + } + } + } + + fun destroyHighlight(id: String) { + uiScope.launch { + this@R2BasicWebView.evaluateJavascript("destroyHighlight(\"$id\");", null) + } + } + + fun createAnnotation(id: String) { + uiScope.launch { + this@R2BasicWebView.evaluateJavascript("createAnnotation(\"$id\");", null) + } + } + + fun rectangleForHighlightWithID(id: String, callback: (String) -> Unit) { + uiScope.launch { + this@R2BasicWebView.evaluateJavascript("rectangleForHighlightWithID(\"$id\");") { + callback(it) + } + } + } + + fun runJavaScript(javascript: String, callback: (String) -> Unit) { + this.evaluateJavascript(javascript) { result -> + callback(result) + } + } + } \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/R2CbzActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/R2CbzActivity.kt deleted file mode 100644 index 2193057f..00000000 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/R2CbzActivity.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Module: r2-navigator-kotlin - * Developers: Aferdita Muriqi, Mostapha Idoubihi - * - * Copyright (c) 2018. Readium Foundation. All rights reserved. - * Use of this source code is governed by a BSD-style license which is detailed in the - * LICENSE file present in the project repository where this source code is maintained. - */ - -package org.readium.r2.navigator - -import android.content.Context -import android.content.SharedPreferences -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.readium.r2.navigator.extensions.layoutDirectionIsRTL -import org.readium.r2.navigator.pager.R2PagerAdapter -import org.readium.r2.navigator.pager.R2ViewPager -import org.readium.r2.shared.Publication -import kotlin.coroutines.CoroutineContext - - -class R2CbzActivity : AppCompatActivity(), CoroutineScope { - /** - * Context of this scope. - */ - override val coroutineContext: CoroutineContext - get() = Dispatchers.Main - - private lateinit var preferences: SharedPreferences - lateinit var resourcePager: R2ViewPager - var resources = arrayListOf() - - private lateinit var publicationPath: String - private lateinit var publication: Publication - private lateinit var cbzName: String - private lateinit var publicationIdentifier: String - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_r2_viewpager) - - preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) - resourcePager = findViewById(R.id.resourcePager) - - publicationPath = intent.getStringExtra("publicationPath") - publication = intent.getSerializableExtra("publication") as Publication - cbzName = intent.getStringExtra("cbzName") - publicationIdentifier = publication.metadata.identifier - title = publication.metadata.title - - for (link in publication.pageList) { - resources.add(link.href.toString()) - } - - val index = preferences.getInt("$publicationIdentifier-document", 0) - - val adapter = R2PagerAdapter(supportFragmentManager, resources, publication.metadata.title, Publication.TYPE.CBZ, publicationPath) - - resourcePager.adapter = adapter - - if (index == 0) { - if (layoutDirectionIsRTL()) { - // The view has RTL layout - resourcePager.currentItem = resources.size - 1 - } else { - // The view has LTR layout - resourcePager.currentItem = index - } - } else { - resourcePager.currentItem = index - } - - toggleActionBar() - } - -// override fun onCreateOptionsMenu(menu: Menu?): Boolean { -// // TODO we could add a thumbnail view here -// return true -// } -// -// override fun onOptionsItemSelected(item: MenuItem): Boolean { -// return super.onOptionsItemSelected(item) -// } - - override fun onPause() { - super.onPause() - val publicationIdentifier = publication.metadata.identifier - val documentIndex = resourcePager.currentItem - preferences.edit().putInt("$publicationIdentifier-document", documentIndex).apply() - } - - fun nextResource(v: View? = null) { - launch { - if (layoutDirectionIsRTL()) { - // The view has RTL layout - resourcePager.currentItem = resourcePager.currentItem - 1 - } else { - // The view has LTR layout - resourcePager.currentItem = resourcePager.currentItem + 1 - } - } - } - - fun previousResource(v: View? = null) { - launch { - if (layoutDirectionIsRTL()) { - // The view has RTL layout - resourcePager.currentItem = resourcePager.currentItem + 1 - } else { - // The view has LTR layout - resourcePager.currentItem = resourcePager.currentItem - 1 - } - - } - } - - fun toggleActionBar(v: View? = null) { - launch { - if (supportActionBar!!.isShowing) { - resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar - or View.SYSTEM_UI_FLAG_IMMERSIVE) - } else { - resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) - } - } - } -} - diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/R2EpubActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/R2EpubActivity.kt deleted file mode 100644 index 45b58f35..00000000 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/R2EpubActivity.kt +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Module: r2-navigator-kotlin - * Developers: Aferdita Muriqi, Clément Baumann - * - * Copyright (c) 2018. Readium Foundation. All rights reserved. - * Use of this source code is governed by a BSD-style license which is detailed in the - * LICENSE file present in the project repository where this source code is maintained. - */ - -package org.readium.r2.navigator - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.viewpager.widget.ViewPager -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.readium.r2.navigator.extensions.layoutDirectionIsRTL -import org.readium.r2.navigator.pager.PageCallback -import org.readium.r2.navigator.pager.R2EpubPageFragment -import org.readium.r2.navigator.pager.R2PagerAdapter -import org.readium.r2.navigator.pager.R2ViewPager -import org.readium.r2.shared.* -import java.net.URI -import kotlin.coroutines.CoroutineContext - - -open class R2EpubActivity : AppCompatActivity(), PageCallback, CoroutineScope { - /** - * Context of this scope. - */ - override val coroutineContext: CoroutineContext - get() = Dispatchers.Main - - - lateinit var preferences: SharedPreferences - lateinit var resourcePager: R2ViewPager - lateinit var resourcesSingle: ArrayList> - lateinit var resourcesDouble: ArrayList> - - lateinit var publicationPath: String - protected lateinit var epubName: String - lateinit var publication: Publication - lateinit var publicationIdentifier: String - - var allowToggleActionBar = true - - var pagerPosition = 0 - - private var currentPagerPosition: Int = 0 - lateinit var adapter:R2PagerAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_r2_viewpager) - - preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) - resourcePager = findViewById(R.id.resourcePager) - resourcesSingle = ArrayList() - resourcesDouble = ArrayList() - - publicationPath = intent.getStringExtra("publicationPath") - publication = intent.getSerializableExtra("publication") as Publication - epubName = intent.getStringExtra("epubName") - publicationIdentifier = publication.metadata.identifier - - title = null - - val port = preferences.getString("$publicationIdentifier-publicationPort", 0.toString())?.toInt() - - // TODO needs work, currently showing two resources for fxl, needs to understand which two resources, left & right, or only right etc. - var doublePageIndex = 0 - var doublePageLeft = "" - var doublePageRight = "" - var resourceIndexDouble = 0 - - for ((resourceIndexSingle, spineItem) in publication.readingOrder.withIndex()) { - val uri: String = if (URI(publicationPath).isAbsolute) { - if (URI(spineItem.href).isAbsolute) { - spineItem.href!! - } else { - publicationPath + spineItem.href - } - } else { - - // uri = applicationContext.getExternalFilesDir(null).path + "/" + epubName + spineItem.href - "$BASE_URL:$port" + "/" + epubName + spineItem.href - } - resourcesSingle.add(Pair(resourceIndexSingle, uri)) - - // add first page to the right, - if (resourceIndexDouble == 0) { - doublePageLeft = "" - doublePageRight = uri - resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, doublePageRight)) - resourceIndexDouble++ - } else { - // add double pages, left & right - if (doublePageIndex == 0) { - doublePageLeft = uri - doublePageIndex = 1 - } else { - doublePageRight = uri - doublePageIndex = 0 - resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, doublePageRight)) - resourceIndexDouble++ - } - } - } - // add last page if there is only a left page remaining - if (doublePageIndex == 1) { - doublePageIndex = 0 - resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, "")) - } - - - if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { - adapter = R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.EPUB, publicationPath) - } else { - adapter = when (preferences.getInt(COLUMN_COUNT_REF, 0)) { - 1 -> { - R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.FXL, publicationPath) - } - 2 -> { - R2PagerAdapter(supportFragmentManager, resourcesDouble, publication.metadata.title, Publication.TYPE.FXL, publicationPath) - } - else -> { - // TODO based on device - // TODO decide if 1 page or 2 page - R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.FXL, publicationPath) - } - } - } - resourcePager.adapter = adapter - - resourcePager.direction = publication.metadata.direction - - if (publication.cssStyle == PageProgressionDirection.rtl.name) { - resourcePager.direction = PageProgressionDirection.rtl.name - } - - val index = preferences.getInt("$publicationIdentifier-document", 0) - resourcePager.currentItem = index - currentPagerPosition = index - - - resourcePager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - - override fun onPageScrollStateChanged(state: Int) { - // Do nothing - } - - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - // Do nothing - } - - override fun onPageSelected(position: Int) { - if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { -// resourcePager.disableTouchEvents = true - } - pagerPosition = 0 - val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment - if (preferences.getBoolean(SCROLL_REF, false)) { - if (currentPagerPosition < position) { - // handle swipe LEFT - currentFragment?.webView?.scrollToStart() - } else if (currentPagerPosition > position) { - // handle swipe RIGHT - currentFragment?.webView?.scrollToEnd() - } - } else { - if (currentPagerPosition < position) { - // handle swipe LEFT - currentFragment?.webView?.setCurrentItem(0, false) - } else if (currentPagerPosition > position) { - // handle swipe RIGHT - currentFragment?.webView?.setCurrentItem(currentFragment.webView.numPages - 1, false) - } - } - storeDocumentIndex() - currentPagerPosition = position // Update current position - } - - }) - - storeDocumentIndex() - - } - - /** - * storeProgression() : save in the preference the last progression in the spine item - */ - fun storeProgression(locations: Locations?) { - storeDocumentIndex() - val publicationIdentifier = publication.metadata.identifier - preferences.edit().putString("$publicationIdentifier-documentLocations", locations?.toJSON().toString()).apply() - } - - /** - * storeDocumentIndex() : save in the preference the last spine item - */ - fun storeDocumentIndex() { - val documentIndex = resourcePager.currentItem - preferences.edit().putInt("$publicationIdentifier-document", documentIndex).apply() - } - - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == 2 && resultCode == Activity.RESULT_OK) { - if (data != null) { - - pagerPosition = 0 - - val locator = data.getSerializableExtra("locator") as Locator - - // Set the progression fetched - storeProgression(locator.locations) - - // href is the link to the page in the toc - var href = locator.href - - if (href.indexOf("#") > 0) { - href = href.substring(0, href.indexOf("#")) - } - - fun setCurrent(resources: ArrayList<*>) { - for (resource in resources) { - if (resource is Pair<*, *>) { - resource as Pair - if (resource.second.endsWith(href)) { - if (resourcePager.currentItem == resource.first) { - // reload webview if it has an anchor - val currentFragent = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment - locator.locations?.fragment?.let { - var anchor = it - if (anchor.startsWith("#")) { - } else { - anchor = "#$anchor" - } - val goto = resource.second + anchor - currentFragent?.webView?.loadUrl(goto) - }?:run { - currentFragent?.webView?.loadUrl(resource.second) - } - } else { - resourcePager.currentItem = resource.first - } - storeDocumentIndex() - break - } - } else { - resource as Triple - if (resource.second.endsWith(href) || resource.third.endsWith(href)) { - resourcePager.currentItem = resource.first - storeDocumentIndex() - break - } - } - } - } - - resourcePager.adapter = adapter - - if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { - setCurrent(resourcesSingle) - } else { - - when (preferences.getInt(COLUMN_COUNT_REF, 0)) { - 1 -> { - setCurrent(resourcesSingle) - } - 2 -> { - setCurrent(resourcesDouble) - } - else -> { - // TODO based on device - // TODO decide if 1 page or 2 page - setCurrent(resourcesSingle) - } - } - } - - if (supportActionBar!!.isShowing) { - resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar - or View.SYSTEM_UI_FLAG_IMMERSIVE) - } - } - } - super.onActivityResult(requestCode, resultCode, data) - } - - - fun nextResource(smoothScroll: Boolean) { - launch { - pagerPosition = 0 - if (resourcePager.currentItem < resourcePager.adapter!!.count - 1 ) { - - resourcePager.setCurrentItem(resourcePager.currentItem + 1, smoothScroll) - - val currentFragent = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment - - if (layoutDirectionIsRTL() || publication.metadata.direction == PageProgressionDirection.rtl.name) { - // The view has RTL layout - currentFragent?.webView?.let { - currentFragent.webView.progression = 1.0 - currentFragent.webView.setCurrentItem(currentFragent.webView.numPages - 1,false) - } - } else { - // The view has LTR layout - currentFragent?.webView?.let { - currentFragent.webView.progression = 0.0 - currentFragent.webView.setCurrentItem(0,false) - } - } - storeDocumentIndex() - } - } - } - - fun previousResource(smoothScroll: Boolean) { - launch { - pagerPosition = 0 - if (resourcePager.currentItem > 0) { - - resourcePager.setCurrentItem(resourcePager.currentItem - 1, smoothScroll) - - val currentFragent = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment - - if (layoutDirectionIsRTL() || publication.metadata.direction == PageProgressionDirection.rtl.name) { - // The view has RTL layout - currentFragent?.webView?.let { - currentFragent.webView.progression = 0.0 - currentFragent.webView.setCurrentItem(0,false) - } - } else { - // The view has LTR layout - currentFragent?.webView?.let { - currentFragent.webView.progression = 1.0 - currentFragent.webView.setCurrentItem(currentFragent.webView.numPages - 1,false) - } - } - storeDocumentIndex() - } - } - } - - - open fun toggleActionBar() { - if (allowToggleActionBar) { - launch { - if (supportActionBar!!.isShowing) { - resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar - or View.SYSTEM_UI_FLAG_IMMERSIVE) - } else { - resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) - } - } - } - } - - override fun onPageChanged(pageIndex: Int, totalPages: Int, url: String) { - //optional - } - - override fun onPageEnded(end: Boolean) { - //optional - } - -} - diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2WebView.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt similarity index 88% rename from r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2WebView.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt index eb942312..a0e70418 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2WebView.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/R2WebView.kt @@ -7,12 +7,15 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.pager +package org.readium.r2.navigator import android.content.Context import android.graphics.Rect import android.util.AttributeSet import android.view.* +import android.view.KeyEvent +import android.view.View +import android.view.ViewGroup import android.view.animation.Interpolator import android.widget.EdgeEffect import android.widget.Scroller @@ -22,8 +25,9 @@ import androidx.core.view.WindowInsetsCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.readium.r2.navigator.BuildConfig.DEBUG import timber.log.Timber - +import kotlin.math.* /** * Created by Aferdita Muriqi on 12/2/17. @@ -38,29 +42,27 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, private val uiScope = CoroutineScope(Dispatchers.Main) @android.webkit.JavascriptInterface - override fun scrollRight() { - super.scrollRight() + override fun scrollRight(animated: Boolean) { + super.scrollRight(animated) uiScope.launch { if (mCurItem < numPages - 1) { mCurItem++ - activity.onPageChanged(mCurItem + 1, numPages, url) + listener.onPageChanged(mCurItem + 1, numPages, url) } } } @android.webkit.JavascriptInterface - override fun scrollLeft() { - super.scrollLeft() + override fun scrollLeft(animated: Boolean) { + super.scrollLeft(animated) uiScope.launch { if (mCurItem > 0) { mCurItem-- - activity.onPageChanged(mCurItem + 1, numPages, url) + listener.onPageChanged(mCurItem + 1, numPages, url) } } } - private val DEBUG = false - private val USE_CACHE = false private val MAX_SETTLE_DURATION = 600 // ms @@ -68,7 +70,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, private val MIN_FLING_VELOCITY = 400 // dips - internal fun getContentWidth(): Int { + private fun getContentWidth(): Int { return this.computeHorizontalScrollRange()//working after load of page } @@ -158,23 +160,23 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, * Indicates that the pager is in an idle, settled state. The current page * is fully in view and no animation is in progress. */ - val SCROLL_STATE_IDLE = 0 + private val SCROLL_STATE_IDLE = 0 /** * Indicates that the pager is currently being dragged by the user. */ - val SCROLL_STATE_DRAGGING = 1 + private val SCROLL_STATE_DRAGGING = 1 /** * Indicates that the pager is in the process of settling to a final position. */ - val SCROLL_STATE_SETTLING = 2 + private val SCROLL_STATE_SETTLING = 2 private val mEndScrollRunnable = Runnable { setScrollState(SCROLL_STATE_IDLE) } private var mScrollState = SCROLL_STATE_IDLE - internal fun initWebPager() { + private fun initWebPager() { setWillNotDraw(false) descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS isFocusable = true @@ -230,13 +232,13 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, .dispatchApplyWindowInsets(getChildAt(i), applied) // Now keep track of any consumed by tracking each dimension's min // value - res.left = Math.min(childInsets.systemWindowInsetLeft, + res.left = min(childInsets.systemWindowInsetLeft, res.left) - res.top = Math.min(childInsets.systemWindowInsetTop, + res.top = min(childInsets.systemWindowInsetTop, res.top) - res.right = Math.min(childInsets.systemWindowInsetRight, + res.right = min(childInsets.systemWindowInsetRight, res.right) - res.bottom = Math.min(childInsets.systemWindowInsetBottom, + res.bottom = min(childInsets.systemWindowInsetBottom, res.bottom) i++ } @@ -264,9 +266,17 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, mScrollState = newState } - + /** + * @return: Int - Returns the horizontal scrolling value to be scrolled by the webview. + * Does not return the device width minus the padding because the value returned by [getContentWidth] + * is sometimes NOT a multiple of '[getMeasuredWidth] - [getPaddingLeft] - [getPaddingRight]' + * (the value is not consistent across devices). + * + * It will instead add a portion of the remaining pixels to the value returned, so that columns will not be + * misaligned. + */ private fun getClientWidth(): Int { - return (measuredWidth - paddingLeft - paddingRight) + 2 + return this.computeHorizontalScrollRange() / numPages } /** @@ -281,14 +291,14 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, fun calculateCurrentItem() { val currentPage = numPages * progression - mCurItem = currentPage.toInt() + mCurItem = abs(currentPage).roundToInt() } - internal fun setCurrentItemInternal(item: Int, smoothScroll: Boolean, always: Boolean) { + private fun setCurrentItemInternal(item: Int, smoothScroll: Boolean, always: Boolean) { setCurrentItemInternal(item, smoothScroll, 0) } - internal fun setCurrentItemInternal(item: Int, smoothScroll: Boolean, velocity: Int) { + private fun setCurrentItemInternal(item: Int, smoothScroll: Boolean, velocity: Int) { if (mFirstLayout) { // We don't have any idea how big we are yet and shouldn't have any pages either. // Just set things up and let the pending layout handle things. @@ -302,8 +312,8 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, private fun scrollToItem(item: Int, smoothScroll: Boolean, velocity: Int, post: Boolean) { - // todo double check why +2 is needed here - val destX = (getClientWidth() * item) + val width = this.computeHorizontalScrollRange() / numPages + val destX = (width * item) if (smoothScroll) { smoothScrollTo(destX, 0, velocity) } else { @@ -313,7 +323,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, } if (post) { - activity.onPageChanged(item + 1, numPages, url) + listener.onPageChanged(item + 1, numPages, url) } @@ -323,11 +333,11 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, // the screen has to travel, however, we don't want this duration to be effected in a // purely linear fashion. Instead, we use this method to moderate the effect that the distance // of travel has on the overall snap duration. - internal fun distanceInfluenceForSnapDuration(f: Float): Float { + private fun distanceInfluenceForSnapDuration(f: Float): Float { var float = f float -= 0.5f // center the values about 0. float *= 0.3f * Math.PI.toFloat() / 2.0f - return Math.sin(float.toDouble()).toFloat() + return sin(float.toDouble()).toFloat() } @@ -338,7 +348,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, * @param y the number of pixels to scroll by on the Y axis * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) */ - internal fun smoothScrollTo(x: Int, y: Int, velocity: Int) { + private fun smoothScrollTo(x: Int, y: Int, velocity: Int) { var v = velocity val sx: Int val wasScrolling = mScroller != null && !mScroller!!.isFinished @@ -368,19 +378,19 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, val width = getClientWidth() val halfWidth = width / 2 - val distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width) + val distanceRatio = min(1f, 1.0f * abs(dx) / width) val distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio) var duration: Int - v = Math.abs(v) + v = abs(v) duration = if (v > 0) { - 4 * Math.round(1000 * Math.abs(distance / v)) + 4 * (1000 * abs(distance / v)).roundToInt() } else { // final float pageWidth = width * mAdapter.getPageWidth(mCurItem); - val pageDelta = Math.abs(dx).toFloat() / (width + mPageMargin) + val pageDelta = abs(dx).toFloat() / (width + mPageMargin) ((pageDelta + 1) * 100).toInt() } - duration = Math.min(duration, MAX_SETTLE_DURATION) + duration = min(duration, MAX_SETTLE_DURATION) // Reset the "scroll started" flag. It will be flipped to true in all places // where we call computeScrollOffset(). @@ -412,7 +422,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, if (!mScroller!!.isFinished) { val currentPage = scrollX / getClientWidth() - mScroller!!.finalX = currentPage * getClientWidth() + mScroller!!.finalX = (currentPage * getClientWidth()) } else { val widthWithMargin = width - paddingLeft - paddingRight + margin val oldWidthWithMargin = oldWidth - paddingLeft - paddingRight + oldMargin @@ -424,7 +434,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, } } else { val ii = infoForPosition(mCurItem) - val scrollOffset: Float = if (ii != null) Math.min(ii.offset, mLastOffset) else 0.0F + val scrollOffset: Float = if (ii != null) min(ii.offset, mLastOffset) else 0.0F val scrollPos = (scrollOffset * (width - paddingLeft - paddingRight)).toInt() if (scrollPos != scrollX) { completeScroll(false) @@ -461,7 +471,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, childLeft = paddingLeft paddingLeft += child.measuredWidth } - Gravity.CENTER_HORIZONTAL -> childLeft = Math.max((width - child.measuredWidth) / 2, + Gravity.CENTER_HORIZONTAL -> childLeft = max((width - child.measuredWidth) / 2, paddingLeft) Gravity.END -> { childLeft = width - paddingRight - child.measuredWidth @@ -474,7 +484,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, childTop = paddingTop paddingTop += child.measuredHeight } - Gravity.CENTER_VERTICAL -> childTop = Math.max((height - child.measuredHeight) / 2, + Gravity.CENTER_VERTICAL -> childTop = max((height - child.measuredHeight) / 2, paddingTop) Gravity.BOTTOM -> { childTop = height - paddingBottom - child.measuredHeight @@ -577,7 +587,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, childLeft = paddingLeft paddingLeft += child.width } - Gravity.CENTER_HORIZONTAL -> childLeft = Math.max((width - child.measuredWidth) / 2, + Gravity.CENTER_HORIZONTAL -> childLeft = max((width - child.measuredWidth) / 2, paddingLeft) Gravity.END -> { childLeft = width - paddingRight - child.measuredWidth @@ -658,12 +668,10 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, if (!mIsBeingDragged) { val pointerIndex = ev.findPointerIndex(mActivePointerId) val x = ev.getX(pointerIndex) - val xDiff = Math.abs(x - mLastMotionX) + val xDiff = abs(x - mLastMotionX) val y = ev.getY(pointerIndex) - val yDiff = Math.abs(y - mLastMotionY) - if (DEBUG) { - Timber.v("Moved x to $x,$y diff=$xDiff,$yDiff") - } + val yDiff = abs(y - mLastMotionY) + if (DEBUG) Timber.v("Moved x to $x,$y diff=$xDiff,$yDiff") if (xDiff > mTouchSlop && xDiff > yDiff) { if (DEBUG) Timber.v("Starting drag!") @@ -679,6 +687,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, } } MotionEvent.ACTION_UP -> if (mIsBeingDragged) { + mIsBeingDragged = false val velocityTracker = mVelocityTracker velocityTracker!!.computeCurrentVelocity(2000, mMaximumVelocity.toFloat()) val initialVelocity = velocityTracker.getXVelocity(mActivePointerId).toInt() @@ -689,7 +698,16 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, val totalDelta = (x - mInitialMotionX).toInt() val nextPage = determineTargetPage(currentPage, 0f, initialVelocity, totalDelta) - setCurrentItemInternal(nextPage, true, initialVelocity) + if (nextPage == currentPage && nextPage == 0 && scrollX == 0) { + if (DEBUG) Timber.tag(this::class.java.simpleName).d("onTouchEvent scrollLeft") + scrollLeft(animated = true) + } else if (nextPage == numPages) { + if (DEBUG) Timber.tag(this::class.java.simpleName).d("onTouchEvent scrollRight") + scrollRight(animated = true) + } else { + if (DEBUG) Timber.tag(this::class.java.simpleName).d("onTouchEvent setCurrentItemInternal") + setCurrentItemInternal(nextPage, true, initialVelocity) + } } MotionEvent.ACTION_CANCEL -> if (mIsBeingDragged) { @@ -763,10 +781,10 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, private fun determineTargetPage(currentPage: Int, pageOffset: Float, velocity: Int, deltaX: Int): Int { val targetPage: Int - val d = Math.abs(deltaX) - val v = Math.abs(velocity) + val d = abs(deltaX) + val v = abs(velocity) - targetPage = if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { + targetPage = if (abs(deltaX) > mFlingDistance && abs(velocity) > mMinimumVelocity) { if (velocity > 0) currentPage else currentPage + 1 } else { val truncator = if (currentPage >= mCurItem) 0.4f else 0.6f @@ -820,7 +838,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, * @param event The key event to execute. * @return Return true if the event was handled, else false. */ - fun executeKeyEvent(event: KeyEvent): Boolean { + private fun executeKeyEvent(event: KeyEvent): Boolean { var handled = false if (event.action == KeyEvent.ACTION_DOWN) { when (event.keyCode) { @@ -851,7 +869,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, * either [View.FOCUS_LEFT] or [View.FOCUS_RIGHT]. * @return Whether the scrolling was handled successfully. */ - fun arrowScroll(direction: Int): Boolean { + private fun arrowScroll(direction: Int): Boolean { var currentFocused: View? = findFocus() if (currentFocused === this) { currentFocused = null @@ -876,7 +894,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, sb.append(" => ").append(parent.javaClass.simpleName) parent = parent.parent } - Timber.e("arrowScroll tried to find focus based on non-child current focused view %s", sb.toString()) + if (DEBUG) Timber.e("arrowScroll tried to find focus based on non-child current focused view %s", sb.toString()) currentFocused = null } } @@ -947,7 +965,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, return outRect } - internal fun pageLeft(): Boolean { + private fun pageLeft(): Boolean { if (mCurItem > 0) { setCurrentItem(mCurItem - 1, true) return true @@ -955,7 +973,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, return false } - internal fun pageRight(): Boolean { + private fun pageRight(): Boolean { if (mCurItem < numPages) { setCurrentItem(mCurItem + 1, true) return true @@ -967,7 +985,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, get() { var numPages = 0 try { - numPages = getContentWidth() / (getClientWidth() - 2) + numPages = this.computeHorizontalScrollRange() / this.computeHorizontalScrollExtent() } catch (e: Exception) { } finally { if (numPages == 0) { @@ -989,7 +1007,7 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, */ var isDecor: Boolean = false - val LAYOUT_ATTRS = intArrayOf(android.R.attr.layout_gravity) + private val LAYOUT_ATTRS = intArrayOf(android.R.attr.layout_gravity) /** * Gravity setting for use on decor views only: @@ -1014,8 +1032,3 @@ class R2WebView(context: Context, attrs: AttributeSet) : R2BasicWebView(context, } } } - -interface PageCallback { - fun onPageChanged(pageIndex: Int, totalPages: Int, url: String) - fun onPageEnded(end: Boolean) -} \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt new file mode 100644 index 00000000..dcfc605c --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2AudiobookActivity.kt @@ -0,0 +1,386 @@ +package org.readium.r2.navigator.audiobook + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.os.Handler +import android.widget.SeekBar +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.lifecycle.Lifecycle +import kotlinx.android.synthetic.main.activity_r2_audiobook.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import org.readium.r2.navigator.* +import org.readium.r2.navigator.BuildConfig.* +import org.readium.r2.shared.Link +import org.readium.r2.shared.Locations +import org.readium.r2.shared.Locator +import org.readium.r2.shared.Publication +import timber.log.Timber +import java.util.concurrent.TimeUnit +import kotlin.coroutines.CoroutineContext + +open class R2AudiobookActivity : AppCompatActivity(), CoroutineScope, IR2Activity, MediaPlayerCallback, VisualNavigator { + + override fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun go(link: Link, animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goForward(animated: Boolean, completion: () -> Unit): Boolean { + if (currentResource < publication.readingOrder.size - 1) { + currentResource++ + } + + mediaPlayer?.next() + play_pause!!.callOnClick() + return true + } + + override fun goBackward(animated: Boolean, completion: () -> Unit): Boolean { + if (currentResource > 0) { + currentResource-- + } + + mediaPlayer?.previous() + play_pause!!.callOnClick() + return true + } + + override val readingProgression: ReadingProgression + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + + override fun goLeft(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goRight(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Context of this scope. + */ + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + + override lateinit var preferences: SharedPreferences + override lateinit var publication: Publication + override lateinit var publicationIdentifier: String + override lateinit var publicationFileName: String + override lateinit var publicationPath: String + override var bookId: Long = -1 + + var currentResource = 0 + + var startTime = 0.0 + private var finalTime = 0.0 + + private val forwardTime = 10000 + private val backwardTime = 10000 + + var mediaPlayer: R2MediaPlayer? = null + + protected var navigatorDelegate: NavigatorDelegate? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_r2_audiobook) + + preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) + + publicationPath = intent.getStringExtra("publicationPath") ?: throw Exception("publicationPath required") + publicationFileName = intent.getStringExtra("publicationFileName") ?: throw Exception("publicationFileName required") + + publication = intent.getSerializableExtra("publication") as Publication + publicationIdentifier = publication.metadata.identifier!! + + title = null + + Handler().postDelayed({ + + if (this.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { + + mediaPlayer = R2MediaPlayer(publication.readingOrder, this) + + mediaPlayer?.goTo(currentResource) + + currentLocation?.locations?.progression?.let { progression -> + mediaPlayer?.seekTo(progression) + seekLocation = currentLocation?.locations + isSeekNeeded = true + } + + seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { + /** + * Notification that the progress level has changed. Clients can use the fromUser parameter + * to distinguish user-initiated changes from those that occurred programmatically. + * + * @param seekBar The SeekBar whose progress has changed + * @param progress The current progress level. This will be in the range min..max where min + * @param fromUser True if the progress change was initiated by the user. + */ + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + if (!fromUser) { + return + } + mediaPlayer?.seekTo(progress) + if (DEBUG) Timber.tag("AUDIO").d("progress $progress") + } + + /** + * Notification that the user has started a touch gesture. Clients may want to use this + * to disable advancing the seekbar. + * @param seekBar The SeekBar in which the touch gesture began + */ + override fun onStartTrackingTouch(seekBar: SeekBar?) { + // do nothing + isSeekTracking = true + if (DEBUG) Timber.tag("AUDIO").d("start tracking") + } + + /** + * Notification that the user has finished a touch gesture. Clients may want to use this + * to re-enable advancing the seekbar. + * @param seekBar The SeekBar in which the touch gesture began + */ + override fun onStopTrackingTouch(seekBar: SeekBar?) { + // do nothing + isSeekTracking = false + if (DEBUG) Timber.tag("AUDIO").d("stop tracking") + } + + }) + + play_pause!!.setOnClickListener { view -> + mediaPlayer?.let { + if (it.isPlaying) { + it.pause() + } else { + if (it.isPaused) { + it.resume() + } else { + it.startPlayer() + } + Handler().postDelayed(updateSeekTime, 100) + } + this.updateUI() + } + } + + play_pause!!.callOnClick() + + fast_forward!!.setOnClickListener { + if (startTime.toInt() + forwardTime <= finalTime) { + startTime += forwardTime + mediaPlayer?.seekTo(startTime) + } + } + + fast_back!!.setOnClickListener { + if (startTime.toInt() - backwardTime > 0) { + startTime -= backwardTime + mediaPlayer?.seekTo(startTime) + } + } + + next_chapter!!.setOnClickListener { view -> + goForward(false) {} + } + + prev_chapter!!.setOnClickListener { view -> + goBackward(false) {} + } + + } + }, 100) + } + + private fun updateUI() { + + if (currentResource == publication.readingOrder.size - 1) { + next_chapter!!.isEnabled = false + next_chapter!!.alpha = .5f + + } else { + next_chapter!!.isEnabled = true + next_chapter!!.alpha = 1.0f + } + if (currentResource == 0) { + prev_chapter!!.isEnabled = false + prev_chapter!!.alpha = .5f + + } else { + prev_chapter!!.isEnabled = true + prev_chapter!!.alpha = 1.0f + } + + val current = publication.readingOrder[currentResource] + chapterView!!.text = current.title + + + if (mediaPlayer!!.isPlaying) { + play_pause!!.setImageDrawable(ContextCompat.getDrawable(this@R2AudiobookActivity, R.drawable.ic_pause_white_24dp)) + } else { + play_pause!!.setImageDrawable(ContextCompat.getDrawable(this@R2AudiobookActivity, R.drawable.ic_play_arrow_white_24dp)) + } + + finalTime = mediaPlayer!!.duration + startTime = mediaPlayer!!.currentPosition + + seekBar!!.max = finalTime.toInt() + + chapterTime!!.text = String.format("%d:%d", + TimeUnit.MILLISECONDS.toMinutes(finalTime.toLong()), + TimeUnit.MILLISECONDS.toSeconds(finalTime.toLong()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(finalTime.toLong()))) + + progressTime!!.text = String.format("%d:%d", + TimeUnit.MILLISECONDS.toMinutes(startTime.toLong()), + TimeUnit.MILLISECONDS.toSeconds(startTime.toLong()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(startTime.toLong()))) + + seekBar!!.progress = startTime.toInt() + + val resource = publication.readingOrder[currentResource] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations(progression = seekBar!!.progress.toDouble()))) + + } + + private var seekLocation: Locations? = null + private var isSeekNeeded = false + var isSeekTracking = false + private fun seekIfNeeded() { + if (isSeekNeeded) { + val time = seekLocation?.fragment?.let { + var time = it + if (time.startsWith("#t=")) { + time = time.substring(time.indexOf('=') + 1) + } + time + } + time?.let { + mediaPlayer?.seekTo(TimeUnit.SECONDS.toMillis(it.toLong()).toInt()) + } ?: run { + seekLocation?.progression?.let { progression -> + mediaPlayer?.seekTo(progression) + } + } + seekLocation = null + isSeekNeeded = false + } + } + + override fun onPrepared() { + seekIfNeeded() + Handler().postDelayed(updateSeekTime, 100) + updateUI() + } + + override fun onComplete(index: Int, currentPosition: Int, duration: Int) { + if (currentResource == index && currentPosition > 0 && currentResource < publication.readingOrder.size - 1 && currentPosition >= duration - 200 && !isSeekTracking) { + Handler().postDelayed({ + if (currentResource < publication.readingOrder.size - 1) { + currentResource++ + } + mediaPlayer?.next() + play_pause!!.callOnClick() + }, 100) + } else if (currentPosition > 0 && currentResource == publication.readingOrder.size - 1) { + mediaPlayer?.pause() + play_pause!!.setImageDrawable(ContextCompat.getDrawable(this@R2AudiobookActivity, R.drawable.ic_play_arrow_white_24dp)) + } else { + mediaPlayer?.pause() + play_pause!!.setImageDrawable(ContextCompat.getDrawable(this@R2AudiobookActivity, R.drawable.ic_play_arrow_white_24dp)) + } + } + + private val updateSeekTime = object : Runnable { + override fun run() { + if (mediaPlayer!!.isPrepared) { + mediaPlayer?.let { + startTime = it.mediaPlayer.currentPosition.toDouble() + } + progressTime!!.text = String.format("%d:%d", + TimeUnit.MILLISECONDS.toMinutes(startTime.toLong()), + TimeUnit.MILLISECONDS.toSeconds(startTime.toLong()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(startTime.toLong()))) + seekBar!!.progress = startTime.toInt() + + val resource = publication.readingOrder[currentResource] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations(progression = seekBar!!.progress.toDouble()))) + + Handler().postDelayed(this, 100) + } + } + } + + override fun onResume() { + super.onResume() + mediaPlayer?.resume() + } + + override fun onPause() { + super.onPause() + mediaPlayer?.pause() + } + + override fun onStop() { + super.onStop() + mediaPlayer?.stop() + } + + override fun onDestroy() { + super.onDestroy() + mediaPlayer?.stop() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == 2 && resultCode == Activity.RESULT_OK) { + if (data != null) { + val locator = data.getSerializableExtra("locator") as Locator + + // Set the progression fetched + navigatorDelegate?.locationDidChange(locator = locator) + + // href is the link to the page in the toc + var href = locator.href + + if (href!!.indexOf("#") > 0) { + href = href.substring(0, href.indexOf("#")) + } + + var index = 0 + for (resource in publication.readingOrder) { + if (resource.href!!.endsWith(href)) { + currentResource = index + break + } + index++ + } + seekLocation = locator.locations + + isSeekNeeded = true + + mediaPlayer?.goTo(currentResource) + + play_pause!!.callOnClick() + + chapterView!!.text = publication.readingOrder[currentResource].title + + } + } + + } + +} diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2MediaPlayer.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2MediaPlayer.kt new file mode 100644 index 00000000..2bcec811 --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/audiobook/R2MediaPlayer.kt @@ -0,0 +1,158 @@ +package org.readium.r2.navigator.audiobook + +import android.app.ProgressDialog +import android.media.MediaPlayer +import android.media.MediaPlayer.OnPreparedListener +import android.net.Uri +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.readium.r2.shared.Link +import java.io.IOException + + +class R2MediaPlayer(private var items: MutableList, private var callback: MediaPlayerCallback) : OnPreparedListener { + + private val uiScope = CoroutineScope(Dispatchers.Main) + + var progress: ProgressDialog? = null + + var mediaPlayer: MediaPlayer = MediaPlayer() + + val isPlaying: Boolean + get() = mediaPlayer.isPlaying + + val duration: Double + get() = mediaPlayer.duration.toDouble() //if (isPrepared) {mediaPlayer.duration.toDouble()}else {0.0} + + val currentPosition: Double + get() = mediaPlayer.currentPosition.toDouble() //if (isPrepared) {mediaPlayer.currentPosition.toDouble()}else {0.0} + + var isPaused: Boolean + var isPrepared: Boolean + + private var index: Int + + init { + isPaused = false + isPrepared = false + index = 0 + if (mediaPlayer.isPlaying) { + mediaPlayer.stop() + mediaPlayer.release() + } + toggleProgress(true) + } + + + /** + * Called when the media file is ready for playback. + * + * @param mp the MediaPlayer that is ready for playback + */ + override fun onPrepared(mp: MediaPlayer?) { + toggleProgress(false) + this.start() + callback.onPrepared() + isPrepared = true + } + + fun startPlayer() { + mediaPlayer.reset() + try { + mediaPlayer.setDataSource(Uri.parse(items[index].href).toString()) + mediaPlayer.setOnPreparedListener(this) + mediaPlayer.prepareAsync() + toggleProgress(true) + } catch (e: IllegalArgumentException) { + e.printStackTrace() + } catch (e: IllegalStateException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } + } + + private fun toggleProgress(show: Boolean) { + uiScope.launch { + if (show) progress?.show() + else progress?.hide() + } + } + + fun seekTo(progression: Any) { + when (progression) { + is Double -> mediaPlayer.seekTo(progression.toInt()) + is Int -> mediaPlayer.seekTo(progression) + else -> mediaPlayer.seekTo(progression.toString().toInt()) + } + } + + fun stop() { + if (isPrepared) { + mediaPlayer.stop() + isPrepared = false + } + } + + fun pause() { + if (isPrepared) { + mediaPlayer.pause() + isPaused = true + } + } + + fun start() { + mediaPlayer.start() + isPaused = false + isPrepared = false + mediaPlayer.setOnCompletionListener { + callback.onComplete(index, it.currentPosition, it.duration) + } + } + + fun resume() { + if (isPrepared) { + mediaPlayer.start() + isPaused = false + } + } + + fun goTo(index: Int) { + this.index = index + isPaused = false + isPrepared = false + if (mediaPlayer.isPlaying) { + mediaPlayer.stop() + } + toggleProgress(true) + } + + fun previous() { + index -= 1 + isPaused = false + isPrepared = false + if (mediaPlayer.isPlaying) { + mediaPlayer.stop() + } + toggleProgress(true) + } + + fun next() { + index += 1 + isPaused = false + isPrepared = false + if (mediaPlayer.isPlaying) { + mediaPlayer.stop() + } + toggleProgress(true) + } + +} + +interface MediaPlayerCallback { + fun onPrepared() + fun onComplete(index: Int, currentPosition: Int, duration: Int) +} + + diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt new file mode 100644 index 00000000..925a3c44 --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/cbz/R2CbzActivity.kt @@ -0,0 +1,221 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Aferdita Muriqi, Mostapha Idoubihi + * + * Copyright (c) 2018. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator.cbz + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.readium.r2.navigator.* +import org.readium.r2.navigator.extensions.layoutDirectionIsRTL +import org.readium.r2.navigator.pager.R2PagerAdapter +import org.readium.r2.navigator.pager.R2ViewPager +import org.readium.r2.shared.Link +import org.readium.r2.shared.Locations +import org.readium.r2.shared.Locator +import org.readium.r2.shared.Publication +import kotlin.coroutines.CoroutineContext + + +open class R2CbzActivity : AppCompatActivity(), CoroutineScope, IR2Activity, VisualNavigator { + + override fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun go(link: Link, animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goForward(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goBackward(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override val readingProgression: ReadingProgression + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + + override fun goLeft(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goRight(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Context of this scope. + */ + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + + override lateinit var preferences: SharedPreferences + override lateinit var resourcePager: R2ViewPager + override lateinit var publicationPath: String + override lateinit var publication: Publication + override lateinit var publicationIdentifier: String + override lateinit var publicationFileName: String + override var bookId: Long = -1 + + var resources = arrayListOf() + lateinit var adapter: R2PagerAdapter + + var currentPagerPosition: Int = 0 + protected var navigatorDelegate: NavigatorDelegate? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_r2_viewpager) + + preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) + resourcePager = findViewById(R.id.resourcePager) + resourcePager.type = Publication.TYPE.CBZ + + publicationPath = intent.getStringExtra("publicationPath") ?: throw Exception("publicationPath required") + publicationFileName = intent.getStringExtra("publicationFileName") ?: throw Exception("publicationFileName required") + publication = intent.getSerializableExtra("publication") as Publication + publicationIdentifier = publication.metadata.identifier!! + title = publication.metadata.title + + for (link in publication.images) { + resources.add(link.href.toString()) + } + + adapter = R2PagerAdapter(supportFragmentManager, resources, publication.metadata.title, Publication.TYPE.CBZ, publicationPath) + + resourcePager.adapter = adapter + + if (currentPagerPosition == 0) { + if (layoutDirectionIsRTL()) { + // The view has RTL layout + resourcePager.currentItem = resources.size - 1 + } else { + // The view has LTR layout + resourcePager.currentItem = currentPagerPosition + } + } else { + resourcePager.currentItem = currentPagerPosition + } + + } + + override fun onPause() { + super.onPause() + val resource = publication.images[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations())) + } + + override fun nextResource(v: View?) { + launch { + if (layoutDirectionIsRTL()) { + // The view has RTL layout + resourcePager.currentItem = resourcePager.currentItem - 1 + } else { + // The view has LTR layout + resourcePager.currentItem = resourcePager.currentItem + 1 + } + val resource = publication.images[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations())) + } + } + + override fun previousResource(v: View?) { + launch { + if (layoutDirectionIsRTL()) { + // The view has RTL layout + resourcePager.currentItem = resourcePager.currentItem + 1 + } else { + // The view has LTR layout + resourcePager.currentItem = resourcePager.currentItem - 1 + } + val resource = publication.images[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations())) + } + } + + override fun toggleActionBar() { + if (allowToggleActionBar) { + launch { + if (supportActionBar!!.isShowing) { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } else { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + } + } + } + } + + override fun toggleActionBar(v: View?) { + toggleActionBar() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == 2 && resultCode == Activity.RESULT_OK) { + if (data != null) { + + val locator = data.getSerializableExtra("locator") as Locator + + // Set the progression fetched + navigatorDelegate?.locationDidChange(locator = locator) + + fun setCurrent(resources: ArrayList<*>) { + for (index in 0 until resources.count()) { + val resource = resources[index] as String + if (resource.endsWith(locator.href!!)) { + resourcePager.currentItem = index + break + } + } + } + + resourcePager.adapter = adapter + + setCurrent(resources) + + if (supportActionBar!!.isShowing && allowToggleActionBar) { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } + } + } + super.onActivityResult(requestCode, resultCode, data) + } + +} + diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/divina/R2DiViNaActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/divina/R2DiViNaActivity.kt new file mode 100755 index 00000000..4a2ab29c --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/divina/R2DiViNaActivity.kt @@ -0,0 +1,107 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Aferdita Muriqi + * + * Copyright (c) 2018. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator.divina + +import android.annotation.SuppressLint +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import android.webkit.WebView +import androidx.appcompat.app.AppCompatActivity +import androidx.webkit.WebViewClientCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.readium.r2.navigator.IR2Activity +import org.readium.r2.navigator.R +import org.readium.r2.navigator.R2BasicWebView +import org.readium.r2.shared.Publication +import kotlin.coroutines.CoroutineContext + + +open class R2DiViNaActivity : AppCompatActivity(), CoroutineScope, IR2Activity { + + /** + * Context of this scope. + */ + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + + override lateinit var preferences: SharedPreferences + override lateinit var publication: Publication + override lateinit var publicationIdentifier: String + override lateinit var publicationPath: String + override lateinit var publicationFileName: String + override var bookId: Long = -1 + + lateinit var divinaWebView: R2BasicWebView + + @SuppressLint("SetJavaScriptEnabled") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_r2_divina) + + preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) + divinaWebView = findViewById(R.id.divinaWebView) + divinaWebView.activity = this + divinaWebView.listener = this + + publicationPath = intent.getStringExtra("publicationPath") ?: throw Exception("publicationPath required") + publicationFileName = intent.getStringExtra("publicationFileName") ?: throw Exception("publicationFileName required") + publication = intent.getSerializableExtra("publication") as Publication + + publicationIdentifier = publication.metadata.identifier!! + title = publication.metadata.title + + // Set up divinaWebView to enable JavaScript and access to local URLs + divinaWebView.settings.javaScriptEnabled = true + divinaWebView.settings.allowFileAccess = true + divinaWebView.settings.allowFileAccessFromFileURLs = true + divinaWebView.webViewClient = object : WebViewClientCompat() { + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + // Define the JS toggleMenu function that will call Android's toggleActionBar +// divinaWebView.evaluateJavascript("window.androidObj = function AndroidClass(){};", null) +// divinaWebView.evaluateJavascript("window.androidObj.toggleMenu = function() { Android.toggleMenu() };", null) + + // Now launch the DiViNa player for the folderPath = publicationPath + divinaWebView.evaluateJavascript("if (player) { player.openDiViNaFromPath('${publicationPath}'); };", null) + } + } + divinaWebView.loadUrl("file:///android_asset/divina/divinaPlayer.html") + divinaWebView.addJavascriptInterface(divinaWebView, "Android") + + } + + override fun toggleActionBar() { + launch { + if (supportActionBar!!.isShowing) { + divinaWebView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } else { + divinaWebView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + } + } + } + + override fun onDestroy() { + super.onDestroy() + divinaWebView.evaluateJavascript("if (player) { player.destroy(); };", null) + } +} + diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Highlightable.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Highlightable.kt new file mode 100644 index 00000000..58c9785f --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Highlightable.kt @@ -0,0 +1,45 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Taehyun Kim, Seongjin Kim + * + * Copyright (c) 2019. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator.epub + +import android.graphics.Rect +import org.readium.r2.shared.Locator + +interface IR2Highlightable { + fun showHighlight(highlight: Highlight) + + fun showHighlights(highlights: Array) + + fun hideHighlightWithID(id: String) + + fun hideAllHighlights() + + fun rectangleForHighlightWithID(id: String, callback: (Rect?) -> Unit) + + fun rectangleForHighlightAnnotationMarkWithID(id: String): Rect? + + fun registerHighlightAnnotationMarkStyle(name: String, css: String) + + fun highlightActivated(id: String) + + fun highlightAnnotationMarkActivated(id: String) +} + +data class Highlight( + val id: String, + val locator: Locator, + val color: Int, + val style: Style, + val annotationMarkStyle: String? = null +) + +enum class Style { + highlight, underline, strikethrough +} \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Selectable.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Selectable.kt new file mode 100644 index 00000000..1ef0395d --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/IR2Selectable.kt @@ -0,0 +1,16 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Taehyun Kim, Seongjin Kim + * + * Copyright (c) 2019. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator.epub + +import org.readium.r2.shared.Locator + +interface IR2Selectable { + fun currentSelection(callback: (Locator?) -> Unit) +} \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt new file mode 100644 index 00000000..faa8a839 --- /dev/null +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/R2EpubActivity.kt @@ -0,0 +1,617 @@ +/* + * Module: r2-navigator-kotlin + * Developers: Aferdita Muriqi, Clément Baumann + * + * Copyright (c) 2018. Readium Foundation. All rights reserved. + * Use of this source code is governed by a BSD-style license which is detailed in the + * LICENSE file present in the project repository where this source code is maintained. + */ + +package org.readium.r2.navigator.epub + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.Color +import android.graphics.Rect +import android.os.Bundle +import android.util.DisplayMetrics +import android.view.ActionMode +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.viewpager.widget.ViewPager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.json.JSONException +import org.json.JSONObject +import org.readium.r2.navigator.* +import org.readium.r2.navigator.R +import org.readium.r2.navigator.extensions.layoutDirectionIsRTL +import org.readium.r2.navigator.pager.R2EpubPageFragment +import org.readium.r2.navigator.pager.R2PagerAdapter +import org.readium.r2.navigator.pager.R2ViewPager +import org.readium.r2.shared.* +import java.net.URI +import kotlin.coroutines.CoroutineContext + + +open class R2EpubActivity : AppCompatActivity(), IR2Activity, IR2Selectable, IR2Highlightable, IR2TTS, CoroutineScope, VisualNavigator { + + override fun progressionDidChange(progression: Double) { + val locator = currentLocation + locator?.locations?.progression = progression + navigatorDelegate?.locationDidChange(locator = locator!!) + } + + override fun go(locator: Locator, animated: Boolean, completion: () -> Unit): Boolean { + + pagerPosition = 0 + + // Set the progression fetched + navigatorDelegate?.locationDidChange(locator = locator) + + // href is the link to the page in the toc + var href = locator.href + + if (href!!.indexOf("#") > 0) { + href = href.substring(0, href.indexOf("#")) + } + + fun setCurrent(resources: ArrayList<*>) { + for (resource in resources) { + if (resource is Pair<*, *>) { + resource as Pair + if (resource.second.endsWith(href)) { + if (resourcePager.currentItem == resource.first) { + // reload webview if it has an anchor + val currentFragent = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + locator.locations?.fragment?.let { + var anchor = it + if (!anchor.startsWith("#")) { + anchor = "#$anchor" + } + val goto = resource.second + anchor + currentFragent?.webView?.loadUrl(goto) + } ?: run { + currentFragent?.webView?.loadUrl(resource.second) + } + } else { + resourcePager.currentItem = resource.first + } + break + } + } else { + resource as Triple + if (resource.second.endsWith(href) || resource.third.endsWith(href)) { + resourcePager.currentItem = resource.first + break + } + } + } + } + + resourcePager.adapter = adapter + + if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { + setCurrent(resourcesSingle) + } else { + + when (preferences.getInt(COLUMN_COUNT_REF, 0)) { + 1 -> { + setCurrent(resourcesSingle) + } + 2 -> { + setCurrent(resourcesDouble) + } + else -> { + // TODO based on device + // TODO decide if 1 page or 2 page + setCurrent(resourcesSingle) + } + } + } + + if (supportActionBar!!.isShowing && allowToggleActionBar) { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } + + return true + } + + override fun go(link: Link, animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goForward(animated: Boolean, completion: () -> Unit): Boolean { + launch { + pagerPosition = 0 + if (resourcePager.currentItem < resourcePager.adapter!!.count - 1) { + + resourcePager.setCurrentItem(resourcePager.currentItem + 1, animated) + + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + + if (layoutDirectionIsRTL() || publication.metadata.direction == PageProgressionDirection.rtl.name) { + // The view has RTL layout + currentFragment?.webView?.let { + currentFragment.webView.progression = 1.0 + currentFragment.webView.setCurrentItem(currentFragment.webView.numPages - 1, false) + } + } else { + // The view has LTR layout + currentFragment?.webView?.let { + currentFragment.webView.progression = 0.0 + currentFragment.webView.setCurrentItem(0, false) + } + } + val resource = publication.readingOrder[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations(progression = currentFragment?.webView?.progression))) + + } + } + return true + } + + override fun goBackward(animated: Boolean, completion: () -> Unit): Boolean { + launch { + pagerPosition = 0 + if (resourcePager.currentItem > 0) { + + resourcePager.setCurrentItem(resourcePager.currentItem - 1, animated) + + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + + if (layoutDirectionIsRTL() || publication.metadata.direction == PageProgressionDirection.rtl.name) { + // The view has RTL layout + currentFragment?.webView?.let { + currentFragment.webView.progression = 0.0 + currentFragment.webView.setCurrentItem(0, false) + } + } else { + // The view has LTR layout + currentFragment?.webView?.let { + currentFragment.webView.progression = 1.0 + currentFragment.webView.setCurrentItem(currentFragment.webView.numPages - 1, false) + } + } + val resource = publication.readingOrder[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + + navigatorDelegate?.locationDidChange(locator = Locator(resourceHref, resourceType, publication.metadata.title, Locations(progression = currentFragment?.webView?.progression))) + + } + } + return true + } + + override val readingProgression: ReadingProgression + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + + override fun goLeft(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun goRight(animated: Boolean, completion: () -> Unit): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + /** + * Context of this scope. + */ + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + + + override lateinit var preferences: SharedPreferences + override lateinit var resourcePager: R2ViewPager + override lateinit var publicationPath: String + override lateinit var publicationFileName: String + override lateinit var publication: Publication + override lateinit var publicationIdentifier: String + override var bookId: Long = -1 + + override var allowToggleActionBar = true + + private lateinit var resourcesSingle: ArrayList> + private lateinit var resourcesDouble: ArrayList> + + var pagerPosition = 0 + + var currentPagerPosition: Int = 0 + lateinit var adapter: R2PagerAdapter + + protected var navigatorDelegate: NavigatorDelegate? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_r2_viewpager) + + preferences = getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE) + resourcePager = findViewById(R.id.resourcePager) + resourcePager.type = Publication.TYPE.EPUB + + resourcesSingle = ArrayList() + resourcesDouble = ArrayList() + + publicationPath = intent.getStringExtra("publicationPath") ?: throw Exception("publicationPath required") + publication = intent.getSerializableExtra("publication") as Publication + publicationFileName = intent.getStringExtra("publicationFileName") ?: throw Exception("publicationFileName required") + publicationIdentifier = publication.metadata.identifier!! + + title = null + + val port = preferences.getString("$publicationIdentifier-publicationPort", 0.toString())?.toInt() + + // TODO needs work, currently showing two resources for fxl, needs to understand which two resources, left & right, or only right etc. + var doublePageIndex = 0 + var doublePageLeft = "" + var doublePageRight = "" + var resourceIndexDouble = 0 + + for ((resourceIndexSingle, spineItem) in publication.readingOrder.withIndex()) { + val uri: String = if (URI(publicationPath).isAbsolute) { + if (URI(spineItem.href).isAbsolute) { + spineItem.href!! + } else { + getAbsolute(spineItem.href!!, publicationPath) + } + } else { + "$BASE_URL:$port" + "/" + publicationFileName + spineItem.href + } + resourcesSingle.add(Pair(resourceIndexSingle, uri)) + + // add first page to the right, + if (resourceIndexDouble == 0) { + doublePageLeft = "" + doublePageRight = uri + resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, doublePageRight)) + resourceIndexDouble++ + } else { + // add double pages, left & right + if (doublePageIndex == 0) { + doublePageLeft = uri + doublePageIndex = 1 + } else { + doublePageRight = uri + doublePageIndex = 0 + resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, doublePageRight)) + resourceIndexDouble++ + } + } + } + // add last page if there is only a left page remaining + if (doublePageIndex == 1) { + doublePageIndex = 0 + resourcesDouble.add(Triple(resourceIndexDouble, doublePageLeft, "")) + } + + + if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { + adapter = R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.EPUB, publicationPath) + resourcePager.type = Publication.TYPE.EPUB + } else { + resourcePager.type = Publication.TYPE.FXL + adapter = when (preferences.getInt(COLUMN_COUNT_REF, 0)) { + 1 -> { + R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.FXL, publicationPath) + } + 2 -> { + R2PagerAdapter(supportFragmentManager, resourcesDouble, publication.metadata.title, Publication.TYPE.FXL, publicationPath) + } + else -> { + // TODO based on device + // TODO decide if 1 page or 2 page + R2PagerAdapter(supportFragmentManager, resourcesSingle, publication.metadata.title, Publication.TYPE.FXL, publicationPath) + } + } + } + resourcePager.adapter = adapter + + resourcePager.direction = publication.metadata.direction + + if (publication.cssStyle == PageProgressionDirection.rtl.name) { + resourcePager.direction = PageProgressionDirection.rtl.name + } + + + + resourcePager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + + override fun onPageScrollStateChanged(state: Int) { + // Do nothing + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + // Do nothing + } + + override fun onPageSelected(position: Int) { +// if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { +// resourcePager.disableTouchEvents = true +// } + pagerPosition = 0 + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + if (preferences.getBoolean(SCROLL_REF, false)) { + if (currentPagerPosition < position) { + // handle swipe LEFT + currentFragment?.webView?.scrollToStart() + } else if (currentPagerPosition > position) { + // handle swipe RIGHT + currentFragment?.webView?.scrollToEnd() + } + } else { + if (currentPagerPosition < position) { + // handle swipe LEFT + currentFragment?.webView?.setCurrentItem(0, false) + } else if (currentPagerPosition > position) { + // handle swipe RIGHT + currentFragment?.webView?.setCurrentItem(currentFragment.webView.numPages - 1, false) + } + } + currentPagerPosition = position // Update current position + } + + }) + + + } + + override fun onWindowStartingActionMode(callback: ActionMode.Callback?, type: Int): ActionMode? { + return super.onWindowStartingActionMode(callback, type) + } + + override fun onActionModeStarted(mode: ActionMode?) { + mode?.menu?.clear() + super.onActionModeStarted(mode) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == 2 && resultCode == Activity.RESULT_OK) { + if (data != null) { + + pagerPosition = 0 + + val locator = data.getSerializableExtra("locator") as Locator + + // Set the progression fetched + navigatorDelegate?.locationDidChange(locator = locator) + + // href is the link to the page in the toc + var href = locator.href + + if (href!!.indexOf("#") > 0) { + href = href.substring(0, href.indexOf("#")) + } + + fun setCurrent(resources: ArrayList<*>) { + for (resource in resources) { + if (resource is Pair<*, *>) { + resource as Pair + if (resource.second.endsWith(href)) { + if (resourcePager.currentItem == resource.first) { + // reload webview if it has an anchor + val currentFragent = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + locator.locations?.fragment?.let { + var anchor = it + if (!anchor.startsWith("#")) { + anchor = "#$anchor" + } + val goto = resource.second + anchor + currentFragent?.webView?.loadUrl(goto) + } ?: run { + currentFragent?.webView?.loadUrl(resource.second) + } + } else { + resourcePager.currentItem = resource.first + } + break + } + } else { + resource as Triple + if (resource.second.endsWith(href) || resource.third.endsWith(href)) { + resourcePager.currentItem = resource.first + break + } + } + } + } + + resourcePager.adapter = adapter + + if (publication.metadata.rendition.layout == RenditionLayout.Reflowable) { + setCurrent(resourcesSingle) + } else { + + when (preferences.getInt(COLUMN_COUNT_REF, 0)) { + 1 -> { + setCurrent(resourcesSingle) + } + 2 -> { + setCurrent(resourcesDouble) + } + else -> { + // TODO based on device + // TODO decide if 1 page or 2 page + setCurrent(resourcesSingle) + } + } + } + + if (supportActionBar!!.isShowing && allowToggleActionBar) { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } + } + } + super.onActivityResult(requestCode, resultCode, data) + } + + + + override fun toggleActionBar() { + if (allowToggleActionBar) { + launch { + if (supportActionBar!!.isShowing) { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + } else { + resourcePager.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) + } + } + } + } + + override fun currentSelection(callback: (Locator?) -> Unit) { + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + + currentFragment?.webView?.getCurrentSelectionInfo { + val selection = JSONObject(it) + val resource = publication.readingOrder[resourcePager.currentItem] + val resourceHref = resource.href ?: "" + val resourceType = resource.typeLink ?: "" + val locations = Locations.fromJSON(selection.getJSONObject("locations")) + val text = LocatorText.fromJSON(selection.getJSONObject("text")) + + val locator = Locator( + resourceHref, + resourceType, + locations = locations, + text = text + ) + callback(locator) + } + + } + + override fun showHighlight(highlight: Highlight) { + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + currentFragment?.webView?.run { + val colorJson = JSONObject().apply { + put("red", Color.red(highlight.color)) + put("green", Color.green(highlight.color)) + put("blue", Color.blue(highlight.color)) + } + createHighlight(highlight.locator.toJSON().toString(), colorJson.toString()) { + if (highlight.annotationMarkStyle.isNullOrEmpty().not()) + createAnnotation(highlight.id) + } + } + } + + override fun showHighlights(highlights: Array) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun hideHighlightWithID(id: String) { + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + currentFragment?.webView?.destroyHighlight(id) + currentFragment?.webView?.destroyHighlight(id.replace("HIGHLIGHT", "ANNOTATION")) + } + + override fun hideAllHighlights() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun rectangleForHighlightWithID(id: String, callback: (Rect?) -> Unit) { + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + + currentFragment?.webView?.rectangleForHighlightWithID(id) { + val rect = JSONObject(it).run { + try { + val display = windowManager.defaultDisplay + val metrics = DisplayMetrics() + display.getMetrics(metrics) + val left = getDouble("left") + val width = getDouble("width") + val top = getDouble("top") * metrics.density + val height = getDouble("height") * metrics.density + Rect(left.toInt(), top.toInt(), width.toInt() + left.toInt(), top.toInt() + height.toInt()) + } catch (e: JSONException) { + null + } + } + callback(rect) + } + } + + override fun rectangleForHighlightAnnotationMarkWithID(id: String): Rect? { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun registerHighlightAnnotationMarkStyle(name: String, css: String) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun highlightActivated(id: String) { + } + + override fun highlightAnnotationMarkActivated(id: String) { + } + + fun createHighlight(color: Int, callback: (Highlight) -> Unit) { + currentSelection { locator -> + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + + val colorJson = JSONObject().apply { + put("red", Color.red(color)) + put("green", Color.green(color)) + put("blue", Color.blue(color)) + } + + currentFragment?.webView?.createHighlight(locator?.toJSON().toString(), colorJson.toString()) { + val json = JSONObject(it) + val id = json.getString("id") + callback( + Highlight( + id, + locator!!, + color, + Style.highlight + ) + ) + } + } + } + + fun createAnnotation(highlight: Highlight?, callback: (Highlight) -> Unit) { + if (highlight != null) { + val currentFragment = ((resourcePager.adapter as R2PagerAdapter).mFragments.get((resourcePager.adapter as R2PagerAdapter).getItemId(resourcePager.currentItem))) as? R2EpubPageFragment + currentFragment?.webView?.createAnnotation(highlight.id) + callback(highlight) + } else { + createHighlight(Color.rgb(150, 150, 150)) { + createAnnotation(it) { highlight -> + callback(highlight) + } + } + } + + } + + override fun onPageLoaded() { + super.onPageLoaded() + } + +} + diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLLayout.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt similarity index 97% rename from r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLLayout.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt index b68904bd..22bea23b 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLLayout.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLLayout.kt @@ -7,7 +7,7 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.fxl +package org.readium.r2.navigator.epub.fxl import android.annotation.SuppressLint import android.content.Context @@ -23,6 +23,9 @@ import android.widget.FrameLayout import androidx.core.view.ViewCompat import com.shopgun.android.utils.NumberUtils import java.util.* +import kotlin.math.min +import kotlin.math.roundToInt +import kotlin.math.roundToLong class R2FXLLayout : FrameLayout { @@ -56,7 +59,7 @@ class R2FXLLayout : FrameLayout { field = if (zoomDuration < 0) DEF_ZOOM_DURATION else zoomDuration } - var isScrollingAllowed = false + private var isScrollingAllowed = false // allow parent views to intercept any touch events that we do not consume var isAllowParentInterceptOnEdge = true @@ -103,7 +106,7 @@ class R2FXLLayout : FrameLayout { val r = RectF() val maxDeltaX = drawRect.width() - viewPortRect.width() if (maxDeltaX < 0) { - val leftEdge = Math.round((viewPortRect.width() - drawRect.width()) / 2).toFloat() + val leftEdge = ((viewPortRect.width() - drawRect.width()) / 2).roundToLong().toFloat() if (leftEdge > drawRect.left) { r.left = 0f r.right = leftEdge - drawRect.left @@ -118,7 +121,7 @@ class R2FXLLayout : FrameLayout { val maxDeltaY = drawRect.height() - viewPortRect.height() if (maxDeltaY < 0) { - val topEdge = Math.round((viewPortRect.height() - drawRect.height()) / 2f).toFloat() + val topEdge = ((viewPortRect.height() - drawRect.height()) / 2f).roundToLong().toFloat() if (topEdge > drawRect.top) { r.top = drawRect.top - topEdge r.bottom = 0f @@ -610,7 +613,7 @@ class R2FXLLayout : FrameLayout { private fun interpolate(): Float { var t = 1f * (System.currentTimeMillis() - mStartTime) / zoomDuration - t = Math.min(1f, t) + t = min(1f, t) return animationInterpolator.getInterpolation(t) } @@ -625,23 +628,23 @@ class R2FXLLayout : FrameLayout { internal fun fling(velocityX: Int, velocityY: Int) { - val startX = Math.round(viewPortRect.left) + val startX = viewPortRect.left.roundToInt() val minX: Int val maxX: Int if (viewPortRect.width() < drawRect.width()) { - minX = Math.round(drawRect.left) - maxX = Math.round(drawRect.width() - viewPortRect.width()) + minX = drawRect.left.roundToInt() + maxX = (drawRect.width() - viewPortRect.width()).roundToInt() } else { maxX = startX minX = maxX } - val startY = Math.round(viewPortRect.top) + val startY = viewPortRect.top.roundToInt() val minY: Int val maxY: Int if (viewPortRect.height() < drawRect.height()) { - minY = Math.round(drawRect.top) - maxY = Math.round(drawRect.bottom - viewPortRect.bottom) + minY = drawRect.top.roundToInt() + maxY = (drawRect.bottom - viewPortRect.bottom).roundToInt() } else { maxY = startY minY = maxY diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLOnDoubleTapListener.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLOnDoubleTapListener.kt similarity index 90% rename from r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLOnDoubleTapListener.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLOnDoubleTapListener.kt index 80c05cf3..4776a925 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLOnDoubleTapListener.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLOnDoubleTapListener.kt @@ -7,9 +7,9 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.fxl +package org.readium.r2.navigator.epub.fxl -class R2FXLOnDoubleTapListener(var threeStep: Boolean) : R2FXLLayout.OnDoubleTapListener { +class R2FXLOnDoubleTapListener(private var threeStep: Boolean) : R2FXLLayout.OnDoubleTapListener { override fun onDoubleTap(view: R2FXLLayout, info: R2FXLLayout.TapInfo): Boolean { try { diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLScroller.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLScroller.kt similarity index 97% rename from r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLScroller.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLScroller.kt index 7c5107c1..e0c20cc0 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLScroller.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLScroller.kt @@ -7,7 +7,7 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.fxl +package org.readium.r2.navigator.epub.fxl import android.content.Context import android.widget.OverScroller diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLUtils.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLUtils.kt similarity index 85% rename from r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLUtils.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLUtils.kt index 81d1338f..f9853b29 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/fxl/R2FXLUtils.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/epub/fxl/R2FXLUtils.kt @@ -7,10 +7,12 @@ * LICENSE file present in the project repository where this source code is maintained. */ -package org.readium.r2.navigator.fxl +package org.readium.r2.navigator.epub.fxl import android.graphics.Rect import android.graphics.RectF +import kotlin.math.roundToInt +import kotlin.math.roundToLong object R2FXLUtils { @@ -41,7 +43,7 @@ object R2FXLUtils { * @param bottom bottom */ fun setRect(rect: RectF, left: Float, top: Float, right: Float, bottom: Float) { - rect.set(Math.round(left).toFloat(), Math.round(top).toFloat(), Math.round(right).toFloat(), Math.round(bottom).toFloat()) + rect.set(left.roundToLong().toFloat(), top.roundToLong().toFloat(), right.roundToLong().toFloat(), bottom.roundToLong().toFloat()) } /** @@ -53,7 +55,7 @@ object R2FXLUtils { * @param bottom bottom */ private fun setRect(rect: Rect, left: Float, top: Float, right: Float, bottom: Float) { - rect.set(Math.round(left), Math.round(top), Math.round(right), Math.round(bottom)) + rect.set(left.roundToInt(), top.roundToInt(), right.roundToInt(), bottom.roundToInt()) } fun setArray(array: FloatArray, rect: Rect) { diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/extensions/ActivityExtensions.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/extensions/Extensions.kt similarity index 60% rename from r2-navigator/src/main/java/org/readium/r2/navigator/extensions/ActivityExtensions.kt rename to r2-navigator/src/main/java/org/readium/r2/navigator/extensions/Extensions.kt index 3c8ed654..caa673d0 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/extensions/ActivityExtensions.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/extensions/Extensions.kt @@ -1,10 +1,25 @@ package org.readium.r2.navigator.extensions import android.app.Activity +import android.content.Context +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat +/** + * Extensions + */ + + /** returns true if the resolved layout direction of the content view in this * activity is ViewCompat.LAYOUT_DIRECTION_RTL. Otherwise false. */ fun Activity.layoutDirectionIsRTL(): Boolean { return ViewCompat.getLayoutDirection(findViewById(android.R.id.content)) == ViewCompat.LAYOUT_DIRECTION_RTL +} + + +@ColorInt +fun Context.color(@ColorRes id: Int): Int { + return ContextCompat.getColor(this, id) } \ No newline at end of file diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2CbzPageFragment.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2CbzPageFragment.kt index 8249ed45..a0b984f6 100755 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2CbzPageFragment.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2CbzPageFragment.kt @@ -23,17 +23,17 @@ import java.io.File class R2CbzPageFragment : androidx.fragment.app.Fragment() { - private val zipFile: String? - get() = arguments!!.getString("zipFile") - private val zipEntry: String? - get() = arguments!!.getString("zipEntry") + private val publication: String? + get() = arguments!!.getString("publication") + private val resource: String? + get() = arguments!!.getString("resource") override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val v = inflater.inflate(R.layout.fragment_page_cbz, container, false) + val v = inflater.inflate(R.layout.viewpager_fragment_cbz, container, false) val imageView = v.findViewById(R.id.imageView) - val blob = ZipUtil.unpackEntry(File(zipFile), zipEntry) + val blob = ZipUtil.unpackEntry(File(publication), resource) blob?.let { val arrayInputStream = ByteArrayInputStream(it) val bitmap = BitmapFactory.decodeStream(arrayInputStream) @@ -45,10 +45,10 @@ class R2CbzPageFragment : androidx.fragment.app.Fragment() { companion object { - fun newInstance(zipFile: String, zipEntry: String): R2CbzPageFragment { + fun newInstance(publication: String, resource: String): R2CbzPageFragment { val args = Bundle() - args.putString("zipFile", zipFile) - args.putString("zipEntry", zipEntry) + args.putString("publication", publication) + args.putString("resource", resource) val fragment = R2CbzPageFragment() fragment.arguments = args return fragment diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt index c3c3d70a..80ac8342 100755 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt @@ -14,6 +14,7 @@ import android.content.Context import android.graphics.Color import android.os.Bundle import android.os.CountDownTimer +import android.util.Base64 import android.util.DisplayMetrics import android.view.KeyEvent import android.view.LayoutInflater @@ -23,15 +24,15 @@ import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.webkit.WebViewClientCompat -import org.json.JSONObject -import org.readium.r2.navigator.R -import org.readium.r2.navigator.R2EpubActivity +import org.readium.r2.navigator.* import org.readium.r2.shared.APPEARANCE_REF import org.readium.r2.shared.Locations -import org.readium.r2.shared.PageProgressionDirection import org.readium.r2.shared.SCROLL_REF +import java.io.IOException +import java.io.InputStream class R2EpubPageFragment : Fragment() { @@ -43,11 +44,12 @@ class R2EpubPageFragment : Fragment() { get() = arguments!!.getString("title") lateinit var webView: R2WebView + lateinit var listener: IR2Activity @SuppressLint("SetJavaScriptEnabled") override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val v = inflater.inflate(R.layout.fragment_page_epub, container, false) + val v = inflater.inflate(R.layout.viewpager_fragment_epub, container, false) val preferences = activity?.getSharedPreferences("org.readium.r2.settings", Context.MODE_PRIVATE)!! // Set text color depending of appearance preference @@ -65,12 +67,13 @@ class R2EpubPageFragment : Fragment() { } } - (v.findViewById(R.id.resource_end) as TextView).visibility = View.GONE (v.findViewById(R.id.book_title) as TextView).text = null webView = v!!.findViewById(R.id.webView) as R2WebView - webView.activity = activity as R2EpubActivity + webView.activity = activity as AppCompatActivity + webView.listener = activity as IR2Activity + webView.navigator = activity as Navigator webView.settings.javaScriptEnabled = true webView.isVerticalScrollBarEnabled = false @@ -87,22 +90,21 @@ class R2EpubPageFragment : Fragment() { var endReached = false webView.setOnOverScrolledCallback(object : R2BasicWebView.OnOverScrolledCallback { - override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) { + override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) { val metrics = DisplayMetrics() webView.activity.windowManager.defaultDisplay.getMetrics(metrics) - val topDecile = webView.contentHeight - 1.15*metrics.heightPixels + val topDecile = webView.contentHeight - 1.15 * metrics.heightPixels val bottomDecile = (webView.contentHeight - metrics.heightPixels).toDouble() when (scrollY) { in topDecile..bottomDecile -> { if (!endReached) { endReached = true - webView.activity.onPageEnded(endReached) + webView.listener.onPageEnded(endReached) when (scrollMode) { true -> { - (v.findViewById(R.id.resource_end) as TextView).visibility = View.VISIBLE } } } @@ -110,10 +112,9 @@ class R2EpubPageFragment : Fragment() { else -> { if (endReached) { endReached = false - webView.activity.onPageEnded(endReached) + webView.listener.onPageEnded(endReached) when (scrollMode) { true -> { - (v.findViewById(R.id.resource_end) as TextView).visibility = View.GONE } } } @@ -142,12 +143,10 @@ class R2EpubPageFragment : Fragment() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) - val currentFragment:R2EpubPageFragment = (webView.activity.resourcePager.adapter as R2PagerAdapter).getCurrentFragment() as R2EpubPageFragment - val previousFragment:R2EpubPageFragment? = (webView.activity.resourcePager.adapter as R2PagerAdapter).getPreviousFragment() as? R2EpubPageFragment - val nextFragment:R2EpubPageFragment? = (webView.activity.resourcePager.adapter as R2PagerAdapter).getNextFragment() as? R2EpubPageFragment + val currentFragment: R2EpubPageFragment = (webView.listener.resourcePager?.adapter as R2PagerAdapter).getCurrentFragment() as R2EpubPageFragment if (this@R2EpubPageFragment.tag == currentFragment.tag) { - var locations = Locations.fromJSON(JSONObject(preferences.getString("${webView.activity.publicationIdentifier}-documentLocations", "{}"))) + var locations = webView.navigator.currentLocation?.locations // TODO this seems to be needed, will need to test more if (url!!.indexOf("#") > 0) { @@ -156,13 +155,13 @@ class R2EpubPageFragment : Fragment() { locations = Locations(fragment = id) } - if (locations.fragment == null) { - locations.progression?.let { progression -> + if (locations?.fragment == null) { + locations?.progression?.let { progression -> currentFragment.webView.progression = progression - if (webView.activity.preferences.getBoolean(SCROLL_REF, false)) { + if (webView.listener.preferences.getBoolean(SCROLL_REF, false)) { - currentFragment.webView.scrollToPosition(progression) + currentFragment.webView.scrollToPosition(progression) } else { (object : CountDownTimer(100, 1) { @@ -175,37 +174,15 @@ class R2EpubPageFragment : Fragment() { } } } - } - nextFragment?.let { - if (this@R2EpubPageFragment.tag == nextFragment.tag){ - if (nextFragment.webView.activity.publication.metadata.direction == PageProgressionDirection.rtl.name) { - // The view has RTL layout - nextFragment.webView.scrollToEnd() - } else { - // The view has LTR layout - nextFragment.webView.scrollToStart() - } - } - } - - previousFragment?.let { - if (this@R2EpubPageFragment.tag == previousFragment.tag){ - if (previousFragment.webView.activity.publication.metadata.direction == PageProgressionDirection.rtl.name) { - // The view has RTL layout - previousFragment.webView.scrollToStart() - } else { - // The view has LTR layout - previousFragment.webView.scrollToEnd() - } - } } + webView.listener.onPageLoaded() } // prevent favicon.ico to be loaded, this was causing NullPointerException in NanoHttp override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { - if (!request.isForMainFrame && request.url.path.endsWith("/favicon.ico")) { + if (!request.isForMainFrame && request.url.path?.endsWith("/favicon.ico") == true) { try { return WebResourceResponse("image/png", null, null) } catch (e: Exception) { @@ -214,24 +191,51 @@ class R2EpubPageFragment : Fragment() { return null } + private fun injectScriptFile(view: WebView?, scriptFile: String) { + val input: InputStream + try { + input = resources.assets.open(scriptFile) + val buffer = ByteArray(input.available()) + input.read(buffer) + input.close() + + // String-ify the script byte-array using BASE64 encoding !!! + val encoded = Base64.encodeToString(buffer, Base64.NO_WRAP) + view?.loadUrl("javascript:(function() {" + + "var parent = document.getElementsByTagName('head').item(0);" + + "var script = document.createElement('script');" + + "script.type = 'text/javascript';" + + // Tell the browser to BASE64-decode the string into your script !!! + "script.innerHTML = window.atob('" + encoded + "');" + + "parent.appendChild(script)" + + "})()") + } catch (e: IOException) { + e.printStackTrace() + } catch (e1: IllegalStateException) { + // not attached to a context + } + + } + + } webView.isHapticFeedbackEnabled = false webView.isLongClickable = false webView.setOnLongClickListener { - true + false } - val locations = Locations.fromJSON(JSONObject(preferences.getString("${webView.activity.publicationIdentifier}-documentLocations", "{}"))) - locations.fragment?.let { + val locations = webView.navigator.currentLocation?.locations + + locations?.fragment?.let { var anchor = it - if (anchor.startsWith("#")) { - } else { + if (!anchor.startsWith("#")) { anchor = "#$anchor" } - val href = resourceUrl + anchor + val href = resourceUrl + anchor webView.loadUrl(href) - }?:run { + } ?: run { webView.loadUrl(resourceUrl) } diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt index bb43b36c..07346282 100755 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt @@ -17,12 +17,15 @@ import android.view.ViewGroup import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.webkit.WebViewClientCompat +import org.readium.r2.navigator.IR2Activity +import org.readium.r2.navigator.Navigator import org.readium.r2.navigator.R -import org.readium.r2.navigator.R2EpubActivity -import org.readium.r2.navigator.fxl.R2FXLLayout -import org.readium.r2.navigator.fxl.R2FXLOnDoubleTapListener +import org.readium.r2.navigator.R2BasicWebView +import org.readium.r2.navigator.epub.fxl.R2FXLLayout +import org.readium.r2.navigator.epub.fxl.R2FXLOnDoubleTapListener class R2FXLPageFragment : Fragment() { @@ -41,7 +44,7 @@ class R2FXLPageFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { secondResourceUrl?.let { - val view: View = inflater.inflate(R.layout.fxlview_double, container, false) + val view: View = inflater.inflate(R.layout.fragment_fxllayout_double, container, false) view.setPadding(0, 0, 0, 0) val r2FXLLayout = view.findViewById(R.id.r2FXLLayout) as R2FXLLayout @@ -50,7 +53,7 @@ class R2FXLPageFragment : Fragment() { r2FXLLayout.addOnTapListener(object : R2FXLLayout.OnTapListener { override fun onTap(view: R2FXLLayout, info: R2FXLLayout.TapInfo): Boolean { - (activity as R2EpubActivity).toggleActionBar() + (activity as IR2Activity).toggleActionBar() return true } }) @@ -63,7 +66,7 @@ class R2FXLPageFragment : Fragment() { return view }?:run { - val view: View = inflater.inflate(R.layout.fxlview_single, container, false) + val view: View = inflater.inflate(R.layout.fragment_fxllayout_single, container, false) view.setPadding(0, 0, 0, 0) val r2FXLLayout = view.findViewById(R.id.r2FXLLayout) as R2FXLLayout @@ -72,7 +75,7 @@ class R2FXLPageFragment : Fragment() { r2FXLLayout.addOnTapListener(object : R2FXLLayout.OnTapListener { override fun onTap(view: R2FXLLayout, info: R2FXLLayout.TapInfo): Boolean { - (activity as R2EpubActivity).toggleActionBar() + (activity as IR2Activity).toggleActionBar() return true } }) @@ -87,7 +90,9 @@ class R2FXLPageFragment : Fragment() { @SuppressLint("SetJavaScriptEnabled") private fun setupWebView(webView: R2BasicWebView, resourceUrl: String?) { - webView.activity = activity as R2EpubActivity + webView.activity = activity as AppCompatActivity + webView.listener = activity as IR2Activity + webView.navigator = activity as Navigator webView.settings.javaScriptEnabled = true webView.isVerticalScrollBarEnabled = false @@ -113,7 +118,7 @@ class R2FXLPageFragment : Fragment() { // prevent favicon.ico to be loaded, this was causing NullPointerException in NanoHttp override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { - if (!request.isForMainFrame && request.url.path.endsWith("/favicon.ico")) { + if (!request.isForMainFrame && request.url.path?.endsWith("/favicon.ico") == true) { try { return WebResourceResponse("image/png", null, null) } catch (e: Exception) { diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FragmentPagerAdapter.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FragmentPagerAdapter.kt index 43bbe8aa..2776617c 100755 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FragmentPagerAdapter.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2FragmentPagerAdapter.kt @@ -124,7 +124,7 @@ abstract class R2FragmentPagerAdapter(private val mFragmentManager: FragmentMana for (i in 0 until mSavedStates.size()) { val entry = mSavedStates.valueAt(i) stateIds[i] = mSavedStates.keyAt(i) - state.putParcelable(java.lang.Long.toString(stateIds[i]), entry) + state.putParcelable(stateIds[i].toString(), entry) } state.putLongArray("states", stateIds) } @@ -150,7 +150,7 @@ abstract class R2FragmentPagerAdapter(private val mFragmentManager: FragmentMana mFragments.clear() if (fss != null) { for (fs in fss) { - mSavedStates.put(fs, bundle.getParcelable(java.lang.Long.toString(fs)) as Fragment.SavedState) + mSavedStates.put(fs, bundle.getParcelable(fs.toString()) as Fragment.SavedState) } } val keys = bundle.keySet() diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2PagerAdapter.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2PagerAdapter.kt index dcf42c2c..33b1b608 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2PagerAdapter.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2PagerAdapter.kt @@ -59,6 +59,7 @@ class R2PagerAdapter(fm: FragmentManager, private val resources: List, priv } } Publication.TYPE.CBZ -> R2CbzPageFragment.newInstance(publicationPath, resources[position] as String) + Publication.TYPE.DiViNa -> TODO() } override fun getCount(): Int { diff --git a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2ViewPager.kt b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2ViewPager.kt index 5346b5ef..3e3089a9 100644 --- a/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2ViewPager.kt +++ b/r2-navigator/src/main/java/org/readium/r2/navigator/pager/R2ViewPager.kt @@ -11,10 +11,16 @@ package org.readium.r2.navigator.pager import android.content.Context import android.util.AttributeSet +import android.view.MotionEvent +import org.readium.r2.navigator.BuildConfig.DEBUG +import org.readium.r2.shared.Publication +import timber.log.Timber class R2ViewPager : R2RTLViewPager { + lateinit var type: Publication.TYPE + constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) @@ -25,5 +31,33 @@ class R2ViewPager : R2RTLViewPager { override fun setCurrentItem(item: Int) { super.setCurrentItem(item, false) } - + + override fun onTouchEvent(ev: MotionEvent): Boolean { + if (DEBUG) Timber.tag(this::class.java.simpleName).d("ev.action ${ev.action}") + if (type == Publication.TYPE.EPUB) { + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + // prevent swipe from view pager directly + if (DEBUG) Timber.tag(this::class.java.simpleName).d("ACTION_DOWN") + return false + } + } + } + return super.onTouchEvent(ev) + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (DEBUG) Timber.tag(this::class.java.simpleName).d("onInterceptTouchEvent ev.action ${ev.action}") + if (type == Publication.TYPE.EPUB) { + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + // prevent swipe from view pager directly + if (DEBUG) Timber.tag(this::class.java.simpleName).d("onInterceptTouchEvent ACTION_DOWN") + return false + } + } + } + return super.onInterceptTouchEvent(ev) + } + } \ No newline at end of file diff --git a/r2-navigator/src/main/res/drawable/baseline_fast_forward_white_24.png b/r2-navigator/src/main/res/drawable/baseline_fast_forward_white_24.png new file mode 100755 index 00000000..20cca5f1 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/baseline_fast_forward_white_24.png differ diff --git a/r2-navigator/src/main/res/drawable/baseline_fast_rewind_white_24.png b/r2-navigator/src/main/res/drawable/baseline_fast_rewind_white_24.png new file mode 100755 index 00000000..62d5d573 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/baseline_fast_rewind_white_24.png differ diff --git a/r2-navigator/src/main/res/drawable/baseline_forward_10_white_24.png b/r2-navigator/src/main/res/drawable/baseline_forward_10_white_24.png new file mode 100755 index 00000000..4028f34f Binary files /dev/null and b/r2-navigator/src/main/res/drawable/baseline_forward_10_white_24.png differ diff --git a/r2-navigator/src/main/res/drawable/baseline_replay_10_white_24.png b/r2-navigator/src/main/res/drawable/baseline_replay_10_white_24.png new file mode 100755 index 00000000..653f5452 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/baseline_replay_10_white_24.png differ diff --git a/r2-navigator/src/main/res/drawable/baseline_search_white_24.png b/r2-navigator/src/main/res/drawable/baseline_search_white_24.png new file mode 100755 index 00000000..c45a71b5 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/baseline_search_white_24.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_forward_5_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_forward_5_white_24dp.png new file mode 100644 index 00000000..01a3cc4c Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_forward_5_white_24dp.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_pause_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_pause_white_24dp.png new file mode 100644 index 00000000..660ac658 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_pause_white_24dp.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_play_arrow_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_play_arrow_white_24dp.png new file mode 100644 index 00000000..be5c062b Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_play_arrow_white_24dp.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_replay_5_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_replay_5_white_24dp.png new file mode 100644 index 00000000..9ec71582 Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_replay_5_white_24dp.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_skip_next_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_skip_next_white_24dp.png new file mode 100644 index 00000000..19c4929c Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_skip_next_white_24dp.png differ diff --git a/r2-navigator/src/main/res/drawable/ic_skip_previous_white_24dp.png b/r2-navigator/src/main/res/drawable/ic_skip_previous_white_24dp.png new file mode 100644 index 00000000..f9186c0b Binary files /dev/null and b/r2-navigator/src/main/res/drawable/ic_skip_previous_white_24dp.png differ diff --git a/r2-navigator/src/main/res/layout/activity_r2_audiobook.xml b/r2-navigator/src/main/res/layout/activity_r2_audiobook.xml new file mode 100644 index 00000000..78aef626 --- /dev/null +++ b/r2-navigator/src/main/res/layout/activity_r2_audiobook.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/r2-navigator/src/main/res/layout/activity_r2_divina.xml b/r2-navigator/src/main/res/layout/activity_r2_divina.xml new file mode 100644 index 00000000..b4c25331 --- /dev/null +++ b/r2-navigator/src/main/res/layout/activity_r2_divina.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/r2-navigator/src/main/res/layout/activity_r2_viewpager.xml b/r2-navigator/src/main/res/layout/activity_r2_viewpager.xml index 87b5ca27..056126cd 100644 --- a/r2-navigator/src/main/res/layout/activity_r2_viewpager.xml +++ b/r2-navigator/src/main/res/layout/activity_r2_viewpager.xml @@ -10,11 +10,9 @@ + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + diff --git a/r2-navigator/src/main/res/layout/fragment_fxllayout_single.xml b/r2-navigator/src/main/res/layout/fragment_fxllayout_single.xml new file mode 100644 index 00000000..1ffae6ce --- /dev/null +++ b/r2-navigator/src/main/res/layout/fragment_fxllayout_single.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + diff --git a/r2-navigator/src/main/res/layout/fxlview_double.xml b/r2-navigator/src/main/res/layout/fxlview_double.xml deleted file mode 100644 index 8bd47036..00000000 --- a/r2-navigator/src/main/res/layout/fxlview_double.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/r2-navigator/src/main/res/layout/fxlview_single.xml b/r2-navigator/src/main/res/layout/fxlview_single.xml deleted file mode 100644 index c17a323f..00000000 --- a/r2-navigator/src/main/res/layout/fxlview_single.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/r2-navigator/src/main/res/layout/spinner_item_font.xml b/r2-navigator/src/main/res/layout/item_spinner_font.xml similarity index 100% rename from r2-navigator/src/main/res/layout/spinner_item_font.xml rename to r2-navigator/src/main/res/layout/item_spinner_font.xml diff --git a/r2-navigator/src/main/res/layout/fragment_page_cbz.xml b/r2-navigator/src/main/res/layout/viewpager_fragment_cbz.xml similarity index 100% rename from r2-navigator/src/main/res/layout/fragment_page_cbz.xml rename to r2-navigator/src/main/res/layout/viewpager_fragment_cbz.xml diff --git a/r2-navigator/src/main/res/layout/fragment_page_epub.xml b/r2-navigator/src/main/res/layout/viewpager_fragment_epub.xml similarity index 61% rename from r2-navigator/src/main/res/layout/fragment_page_epub.xml rename to r2-navigator/src/main/res/layout/viewpager_fragment_epub.xml index 8dbf4f92..a7813241 100755 --- a/r2-navigator/src/main/res/layout/fragment_page_epub.xml +++ b/r2-navigator/src/main/res/layout/viewpager_fragment_epub.xml @@ -12,9 +12,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:paddingBottom="40dp" - android:paddingTop="60dp"> + android:orientation="vertical"> - - - + app:layout_constraintTop_toBottomOf="@+id/book_title"/> \ No newline at end of file diff --git a/r2-navigator/src/main/res/values/strings.xml b/r2-navigator/src/main/res/values/strings.xml index ce7f51d9..940bebfc 100644 --- a/r2-navigator/src/main/res/values/strings.xml +++ b/r2-navigator/src/main/res/values/strings.xml @@ -9,7 +9,8 @@ - end of chapter ~ ~ ~ + 00:00 +