commit fb74b92de14dc921a3689eeca200c485acd1d53c Author: Lex Lim Date: Fri May 20 08:52:30 2022 +0800 Initialize project diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ca7b603 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2022 Hyperzlib + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..9701030 --- /dev/null +++ b/extension.json @@ -0,0 +1,78 @@ +{ + "name": "Isekai Background Widget", + "namemsg": "isekai-background-widget", + "author": "Hyperzlib", + "version": "1.0.0", + "url": "https://www.isekai.cn", + "descriptionmsg": "isekai-background-widget-desc", + "license-name": "GPL-2.0-or-later", + "type": "parserhook", + "requires": { + + }, + "MessagesDirs": { + "IsekaiBackgroundWidget": [ + "i18n" + ] + }, + "DefaultUserOptions": { + "isekai-bgimg-visible-desktop": true, + "isekai-bgimg-visible-mobile": true + }, + "AutoloadClasses": { + "Isekai\\BackgroundWidget": "includes/BackgroundWidget.php" + }, + "Hooks": { + "ParserFirstCallInit": [ + "Isekai\\BackgroundWidget::onParserSetup" + ], + "BeforePageDisplay": [ + "Isekai\\BackgroundWidget::addModules" + ], + "GetPreferences": [ + "Isekai\\BackgroundWidget::onGetPreferences" + ] + }, + "ResourceModules": { + "ext.isekai.backgroundWidget": { + "scripts": "ext.isekai.backgroundWidget/BgImage.js", + "styles": "ext.isekai.backgroundWidget/global.less", + "targets": [ + "desktop", + "mobile" + ], + "messages": [ + "isekai-background-widget-param-error" + ] + }, + "ext.isekai.backgroundWidget-timeless": { + "scripts": "ext.isekai.backgroundWidget/timeless.js", + "styles": "ext.isekai.backgroundWidget/timeless.less", + "targets": [ + "desktop", + "mobile" + ] + }, + "ext.isekai.backgroundWidget-minerva": { + "scripts": "ext.isekai.backgroundWidget/minerva.js", + "styles": "ext.isekai.backgroundWidget/minerva.less", + "targets": [ + "desktop", + "mobile" + ] + }, + "ext.isekai.backgroundWidget-vector": { + "scripts": "ext.isekai.backgroundWidget/vector.js", + "styles": "ext.isekai.backgroundWidget/vector.less", + "targets": [ + "desktop", + "mobile" + ] + } + }, + "ResourceFileModulePaths": { + "localBasePath": "modules", + "remoteExtPath": "Isekai/modules" + }, + "manifest_version": 1 +} \ No newline at end of file diff --git a/i18n/zh-hans.json b/i18n/zh-hans.json new file mode 100644 index 0000000..b267064 --- /dev/null +++ b/i18n/zh-hans.json @@ -0,0 +1,9 @@ +{ + "isekai-background-widget": "异世界百科 页面背景图片", + "isekai-background-widget-desc": "设置页面的背景图片", + "isekai-background-widget-param-error": "背景图片参数错误:", + + "prefs-isekai-bgimg": "页面背景图片", + "isekai-perfs-bgimg-visible-desktop": "在桌面设备上显示", + "isekai-perfs-bgimg-visible-mobile": "在移动设备上显示" +} \ No newline at end of file diff --git a/includes/BackgroundWidget.php b/includes/BackgroundWidget.php new file mode 100644 index 0000000..78f4e70 --- /dev/null +++ b/includes/BackgroundWidget.php @@ -0,0 +1,76 @@ +setHook('background', self::class . '::onCreateBackgroundWidget'); + return true; + } + + public static function onCreateBackgroundWidget($text, array $args, \Parser $parser, \PPFrame $frame){ + /** @var \ParserOutput */ + $output = $parser->getOutput(); + + $output->addModules('ext.isekai.backgroundWidget'); + + $allowKeys = ['src', 'xcenter', 'ycenter']; + + $args = array_filter($args, function($key) use($allowKeys){ + return in_array($key, $allowKeys); + }, ARRAY_FILTER_USE_KEY); + + foreach($args as $key => $one){ + $args[$key] = $parser->recursiveTagParse($one, $frame); + + if($key == 'xcenter' || $key == 'ycenter'){ + $args[$key] = intval($args[$key]); + } + } + + if(isset($args['src'])){ + if(preg_match('/]src="(?.*?)"/' , $args['src'], $matches) + && isset($matches['file'])){ + $args['src'] = $matches['file']; + } + $output->addJsConfigVars('isekaiBackgroundWidgetData', $args); + } + + return ''; + } + + public static function addModules(\OutputPage $output){ + if(in_array('ext.isekai.backgroundWidget', $output->getModules())){ + //$output->addBodyClasses('has-bgimg'); + + switch(strtolower($output->getSkin()->getSkinName())){ + case 'timeless': + $output->addModules('ext.isekai.backgroundWidget-timeless'); + break; + case 'minerva': + $output->addModules('ext.isekai.backgroundWidget-minerva'); + break; + case 'vector': + $output->addModules('ext.isekai.backgroundWidget-vector'); + break; + } + } + } + + public static function onGetPreferences(\User $user, array &$preferences){ + $preferences['isekai-bgimg-visible-desktop'] = [ + 'type' => 'toggle', + 'label-message' => 'isekai-perfs-bgimg-visible-desktop', + 'section' => 'rendering/isekai-bgimg', + ]; + + $preferences['isekai-bgimg-visible-mobile'] = [ + 'type' => 'toggle', + 'label-message' => 'isekai-perfs-bgimg-visible-mobile', + 'section' => 'rendering/isekai-bgimg', + ]; + } +} \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/BgImage.js b/modules/ext.isekai.backgroundWidget/BgImage.js new file mode 100644 index 0000000..fa933ab --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/BgImage.js @@ -0,0 +1 @@ +!function(t){var e={};function i(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=t,i.c=e,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},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 n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},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=2)}([function(t,e){var i;i=function(){return this}();try{i=i||new Function("return this")()}catch(t){"object"==typeof window&&(i=window)}t.exports=i},function(t,e,i){!function(t){"use strict";function e(t){return(e="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)}var i=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],n=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function r(t,i,n,r,s){if("string"==typeof t&&(t=document.getElementById(t)),!(t&&"object"===e(t)&&"getContext"in t))throw new TypeError("Expecting canvas with `getContext` method in processCanvasRGB(A) calls!");var o=t.getContext("2d");try{return o.getImageData(i,n,r,s)}catch(t){throw new Error("unable to access image data: "+t)}}function s(t,e,i,n,s,a){if(!(isNaN(a)||a<1)){a|=0;var h=r(t,e,i,n,s);h=o(h,0,0,n,s,a),t.getContext("2d").putImageData(h,e,i)}}function o(t,e,r,s,o,a){var h,l,u,d,f,c,m,p,b,y,w,x,S,v,B,O,R,z,k,C,I,P,E,j,A,_=t.data,M=2*a+1,W=s-1,G=o-1,L=a+1,N=L*(L+1)/2,D=new g,H=D;for(u=1;u>$,0!==E?(E=255/E,_[c]=(p*U>>$)*E,_[c+1]=(b*U>>$)*E,_[c+2]=(y*U>>$)*E):_[c]=_[c+1]=_[c+2]=0,p-=x,b-=S,y-=v,w-=B,x-=T.r,S-=T.g,v-=T.b,B-=T.a,d=m+((d=h+a+1)>$,E>0?(E=255/E,_[d]=(p*U>>$)*E,_[d+1]=(b*U>>$)*E,_[d+2]=(y*U>>$)*E):_[d]=_[d+1]=_[d+2]=0,p-=x,b-=S,y-=v,w-=B,x-=T.r,S-=T.g,v-=T.b,B-=T.a,d=h+((d=l+L)>H,P[c+1]=b*D>>H,P[c+2]=y*D>>H,p-=w,b-=x,y-=S,w-=L.r,x-=L.g,S-=L.b,d=m+((d=h+a+1)>H,P[d+1]=b*D>>H,P[d+2]=y*D>>H,p-=w,b-=x,y-=S,w-=L.r,x-=L.g,S-=L.b,d=h+((d=l+_)').find('style[data-type="bgimg-styles"]'),this.posStyles=$("head").append('').find('style[data-type="bgimg-styles-pos"]'),this.domStyleSheet=this.domStyles[0].sheet,this.posStyleSheet=this.posStyles[0].sheet,this.hasBlurArea=!1,this.blurRadius=20,this.blurSource=null,this.img=new Image,this.img.crossOrigin="anonymous",this.img.addEventListener("load",this.onImageLoaded.bind(this)),this.imgRatio=0,this.imgOffset={top:0,left:0},this.imgSize={width:0,height:0},this.viewports=[],window.addEventListener("resize",this.onWindowResize.bind(this))}async loadImage(t,e,i){try{await fetch(t)}catch(t){this.isCrosBlocked=!0,this.img.crossOrigin=null}this.source=t,this.xCenter=e||50,this.yCenter=i||50,this.img.src=t}generateBlurImage(){return new Promise((t,i)=>{let n=document.createElement("canvas");n.width=this.img.width,n.height=this.img.height,n.getContext("2d").drawImage(this.img,0,0),e.canvasRGBA(n,0,0,n.width,n.height,this.blurRadius),n.toBlob(e=>{this.blurSource=URL.createObjectURL(e),t()})})}async onImageLoaded(){if(this.imgRatio=this.img.width/this.img.height,this.hasBlurArea&&(this.isCrosBlocked?this.blurSource=this.source:await this.generateBlurImage()),this.fixPosition(),!this.loaded){this.viewports.forEach(t=>{t.css({backgroundImage:"url('"+this.source+"')",backgroundAttachement:"fixed",opacity:1})});var t=".has-bgimg .bgimg-background-blur::after { ";t+="opacity: 1; background-image: url('"+this.blurSource+"'); ",this.isCrosBlocked&&(t+="filter: blur("+this.blurRadius+"px); "),t+=";",this.domStyleSheet.insertRule(t,this.domStyleSheet.rules.length),this.loaded=!0}}onWindowResize(){this.loaded&&this.fixPosition()}getOverflowSize(t,e,i){let n=e-t,r=e*i/100-t/2;return Math.max(0,Math.min(n,r))}fixPosition(){let t=window.innerWidth/window.innerHeight;this.imgRatio>t?(this.imgSize.height=window.innerHeight,this.imgSize.width=this.imgSize.height*this.imgRatio,this.imgOffset.top=0,this.imgOffset.left=-this.getOverflowSize(window.innerWidth,this.imgSize.width,this.xCenter)):(this.imgSize.width=window.innerWidth,this.imgSize.height=this.imgSize.width/this.imgRatio,this.imgOffset.left=0,this.imgOffset.top=-this.getOverflowSize(window.innerHeight,this.imgSize.height,this.yCenter)),this.fixViewportPosition()}fixViewportPosition(){if(this.viewports.forEach(t=>{t.css({backgroundSize:this.imgSize.width+"px "+this.imgSize.height+"px",backgroundPosition:this.imgOffset.left+"px "+this.imgOffset.top+"px"})}),this.hasBlurArea){let t=".has-bgimg .bgimg-background-blur::after { ";t+="background-size: "+this.imgSize.width+"px "+this.imgSize.height+"px; ",t+="background-position: "+this.imgOffset.left+"px "+this.imgOffset.top+"px; ",t+="}",this.posStyleSheet.insertRule(t,this.posStyleSheet.rules.length),this.posStyleSheet.rules.length>1&&this.posStyleSheet.deleteRule(this.posStyleSheet.rules.length-2)}}addViewport(t){t.css("opacity",0),this.viewports.push(t)}addBlurBackground(t){t.addClass("bgimg-background-blur"),this.hasBlurArea=!0}}}.call(this,i(0))}]); \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/global.less b/modules/ext.isekai.backgroundWidget/global.less new file mode 100644 index 0000000..277e59f --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/global.less @@ -0,0 +1,30 @@ +@globalTransition: opacity 250ms linear; + +.has-bgimg { + .bgimg-background-blur { + z-index: 1; + + &::before { + position: absolute; + content: ''; + z-index: -1; + width: 100%; + height: 100%; + left: 0; + top: 0; + } + + &::after { + position: absolute; + content: ''; + z-index: -2; + transition: @globalTransition; + opacity: 0; + width: 100%; + height: 100%; + left: 0; + top: 0; + background-attachment: fixed; + } + } +} \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/minerva.js b/modules/ext.isekai.backgroundWidget/minerva.js new file mode 100644 index 0000000..26de143 --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/minerva.js @@ -0,0 +1,56 @@ +$(function(){ + function isMobile(){ + var userAgentInfo = navigator.userAgent; + var mobileAgents = ["Android", "iPhone", + "SymbianOS", "Windows Phone", + "iPad", "iPod"]; + var flag = true; + for(var i = 0; i < mobileAgents.length; i ++){ + if (userAgentInfo.indexOf(mobileAgents[isFinite]) != -1) { + return true; + } + } + return false; + } + + function initBackgroundImage(params){ + let bgImg = new isekai.BgImage(); + + bgImg.addBlurBackground($('#mw-mf-page-center')); + + if(!('xcenter' in params)){ + params.xcenter = 50; + } + if(!('ycenter' in params)){ + params.ycenter = 50; + } + + bgImg.loadImage(params.src, params.xcenter, params.ycenter); + } + + var showBackgroundWidget = true; + if(isMobile() && !mw.user.options.get('isekai-bgimg-visible-mobile', true)){ + showBackgroundWidget = false; + } else if(!mw.user.options.get('isekai-bgimg-visible-desktop', true)) { + showBackgroundWidget = false; + } + + if(showBackgroundWidget){ + var params = mw.config.get('isekaiBackgroundWidgetData'); + if(params && params.src){ + $('body').addClass('has-bgimg'); + + if(window.requestAnimationFrame){ + window.requestAnimationFrame(function(){ + initBackgroundImage(params); + }); + } else { + setTimeout(function(){ + initBackgroundImage(params); + }, 0); + } + } else { + console.log(mw.message('isekai-background-widget-param-error').parse(), params); + } + } +}); \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/minerva.less b/modules/ext.isekai.backgroundWidget/minerva.less new file mode 100644 index 0000000..1e84250 --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/minerva.less @@ -0,0 +1,40 @@ +@globalRadius: 0.4em; +@globalBgColor: rgba(255, 255, 255, 0.85); +@headerBgColor: rgba(242, 242, 242, 0.8); +@footerBgColor: rgba(255, 255, 255, 0.75); +@globalBoxShadow: 0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12); +@globalTransition: opacity 250ms linear; +@headerBlurDepth: 10px; + +.has-bgimg { + .isekai-bgimg { + position: fixed; + opacity: 0; + transition: @globalTransition; + + z-index: -2; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + } + + #mw-mf-page-center { + background-color: transparent; + position: relative; + } + + .header-container.header-chrome { + background-color: @headerBgColor; + position: relative; + } + + .minerva-footer { + background-color: @footerBgColor; + position: relative; + } + + #content { + background-color: @globalBgColor; + } +} \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/timeless.js b/modules/ext.isekai.backgroundWidget/timeless.js new file mode 100644 index 0000000..06e4dc9 --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/timeless.js @@ -0,0 +1,64 @@ +$(function(){ + function isMobile(){ + var userAgentInfo = navigator.userAgent; + var mobileAgents = ["Android", "iPhone", + "SymbianOS", "Windows Phone", + "iPad", "iPod"]; + var flag = true; + for(var i = 0; i < mobileAgents.length; i ++){ + if (userAgentInfo.indexOf(mobileAgents[i]) != -1) { + return true; + } + } + return false; + } + + function initBackgroundImage(params){ + let bgImg = new isekai.BgImage(); + + let bgImgElement = $('body').append('
').find('div.isekai-bgimg:last'); + let navbarBg = $('body').append('
').find('div.isekai-navbar-bg:last'); + + bgImg.addViewport(bgImgElement); + + bgImg.addBlurBackground(navbarBg); + bgImg.addBlurBackground($('.sidebar-chunk')); + bgImg.addBlurBackground($('#mw-content')); + bgImg.addBlurBackground($('#mw-content-container')); + bgImg.addBlurBackground($('#mw-footer-container')); + + if(!('xcenter' in params)){ + params.xcenter = 50; + } + if(!('ycenter' in params)){ + params.ycenter = 50; + } + + bgImg.loadImage(params.src, params.xcenter, params.ycenter); + } + + var showBackgroundWidget = true; + if(isMobile() && !mw.user.options.get('isekai-bgimg-visible-mobile', true)){ + showBackgroundWidget = false; + } else if(!mw.user.options.get('isekai-bgimg-visible-desktop', true)) { + showBackgroundWidget = false; + } + + if(showBackgroundWidget){ + var params = mw.config.get('isekaiBackgroundWidgetData'); + if(params && params.src){ + $('body').addClass('has-bgimg'); + if(window.requestAnimationFrame){ + window.requestAnimationFrame(function(){ + initBackgroundImage(params); + }); + } else { + setTimeout(function(){ + initBackgroundImage(params); + }, 0); + } + } else { + console.error(mw.message('isekai-background-widget-param-error').parse(), params); + } + } +}); \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/timeless.less b/modules/ext.isekai.backgroundWidget/timeless.less new file mode 100644 index 0000000..6077fbd --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/timeless.less @@ -0,0 +1,157 @@ +@globalRadius: 0.4em; +@globalBgColor: rgba(255, 255, 255, 0.75); +@headerBgColor: rgba(252, 252, 252, 0.75); +@footerBgColor: rgba(149, 149, 149, 0.7); +@globalBoxShadow: 0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12); +@globalTransition: opacity 250ms linear; +@headerBlurDepth: 10px; + +.has-bgimg { + .isekai-bgimg { + position: fixed; + opacity: 0; + transition: @globalTransition; + + z-index: -2; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + } + + .isekai-navbar-bg { + position: fixed; + z-index: 99; + top: 0; + left: 0; + width: 100%; + height: 3.125em; + } + + body { + background: #f3f3f3; + } + + #mw-content-container { + background: none; + } + + #mw-footer-container { + position: relative; + + &::before { + background: @footerBgColor; + } + } + + #mw-content { + background: none; + border: none; + border-bottom-left-radius: @globalRadius; + border-bottom-right-radius: @globalRadius; + box-shadow: @globalBoxShadow; + + &::before { + background: @globalBgColor; + } + } + + #mw-content-container::after { + display: none; + } + + #mw-header-container { + background: @headerBgColor; + } + + #simpleSearch { + background: rgba(0, 0, 0, 0.07); + border: solid 1px #a2a9b1; + box-shadow: none; + + #searchInput-container input[type="search"]:focus { + background: #fff; + } + } + + #mw-content { + position: relative; + } + + @media screen and (max-width: 850px) { + .isekai-navbar-bg { + display: none; + } + } + + @media screen and (max-width: 1099px) { + #mw-content { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border: none; + background: none; + box-shadow: none; + + &::before { + background: none; + } + + &::after { + display: none; + } + } + + #mw-content-block { + background: none; + } + + #mw-content-container { + &::before { + background: @globalBgColor; + } + + &::after { + display: block; + } + } + + #mw-header-nav-hack { + background: rgba(255,255,255,0.5); + } + + #mw-header-hack { + z-index: 99; + } + + .isekai-bgimg { + display: none; + } + } + + @media screen and (min-width: 1100px) { + #mw-site-navigation .sidebar-chunk, + #mw-related-navigation .sidebar-chunk { + position: relative; + border: none; + border-radius: @globalRadius; + box-shadow: @globalBoxShadow; + + &::before { + background: @globalBgColor; + } + } + } + + @media screen and (max-width: 1099px) { + #mw-site-navigation .sidebar-chunk, + #mw-related-navigation .sidebar-chunk { + &::after { + display: none; + } + } + } + + .comment-body textarea, .comment-preview { + background: @globalBgColor; + } +} \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/vector.js b/modules/ext.isekai.backgroundWidget/vector.js new file mode 100644 index 0000000..f7fca56 --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/vector.js @@ -0,0 +1,66 @@ +$(function(){ + function isMobile(){ + var userAgentInfo = navigator.userAgent; + var mobileAgents = ["Android", "iPhone", + "SymbianOS", "Windows Phone", + "iPad", "iPod"]; + var flag = true; + for(var i = 0; i < mobileAgents.length; i ++){ + if (userAgentInfo.indexOf(mobileAgents[i]) != -1) { + return true; + } + } + return false; + } + + function initBackgroundImage(params){ + let bgImg = new isekai.BgImage(); + + let bgImgElement = $('body').append('
').find('div.isekai-bgimg:last'); + + bgImg.addViewport(bgImgElement); + + bgImg.addBlurBackground($('#mw-panel .portal')); + bgImg.addBlurBackground($('#p-personal')); + bgImg.addBlurBackground($('#footer')); + bgImg.addBlurBackground($('#left-navigation')); + bgImg.addBlurBackground($('#right-navigation')); + bgImg.addBlurBackground($('#content')); + + if(!('xcenter' in params)){ + params.xcenter = 50; + } + if(!('ycenter' in params)){ + params.ycenter = 50; + } + + bgImg.loadImage(params.src, params.xcenter, params.ycenter); + } + + var showBackgroundWidget = true; + if(isMobile() && !mw.user.options.get('isekai-bgimg-visible-mobile', true)){ + showBackgroundWidget = false; + } else if(!mw.user.options.get('isekai-bgimg-visible-desktop', true)) { + showBackgroundWidget = false; + } + + + + if(showBackgroundWidget){ + var params = mw.config.get('isekaiBackgroundWidgetData'); + if(params && params.src){ + $('body').addClass('has-bgimg'); + if(window.requestAnimationFrame){ + window.requestAnimationFrame(function(){ + initBackgroundImage(params); + }); + } else { + setTimeout(function(){ + initBackgroundImage(params); + }, 0); + } + } else { + console.log(mw.message('isekai-background-widget-param-error').parse(), params); + } + } +}); \ No newline at end of file diff --git a/modules/ext.isekai.backgroundWidget/vector.less b/modules/ext.isekai.backgroundWidget/vector.less new file mode 100644 index 0000000..460b544 --- /dev/null +++ b/modules/ext.isekai.backgroundWidget/vector.less @@ -0,0 +1,83 @@ +@globalRadius: 0.4em; +@globalBgColor: rgba(255, 255, 255, 0.75); +@headerBgColor: rgba(252, 252, 252, 0.6); +@footerBgColor: rgba(149, 149, 149, 0.7); +@globalBoxShadow: 0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px 0 rgba(0,0,0,.14),0 1px 14px 0 rgba(0,0,0,.12); +@globalTransition: opacity 250ms linear; +@headerBlurDepth: 10px; + +.has-bgimg { + .isekai-bgimg { + position: fixed; + opacity: 0; + transition: @globalTransition; + + z-index: -2; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + } + + #mw-panel { + .portal { + position: relative; + margin: 0; + padding-right: 0.6em; + padding-left: 0.7em; + &::before { + background-color: @headerBgColor; + } + + h3 { + color: #222; + } + } + } + + #p-personal, + #left-navigation, + #right-navigation { + &::before { + background-color: @headerBgColor; + } + } + + #left-navigation, + #right-navigation { + position: relative; + } + + #mw-page-base { + background: none; + } + + #content { + background: transparent; + position: relative; + + &::before { + background-color: @globalBgColor; + } + } + + #footer { + position: relative; + + &::before { + background-color: @footerBgColor; + } + + ul li { + color: #fff; + + a { + color: #ccddff; + + &:visited { + color: #ccddff; + } + } + } + } +} \ No newline at end of file diff --git a/src/BgImage.js b/src/BgImage.js new file mode 100644 index 0000000..b23e91d --- /dev/null +++ b/src/BgImage.js @@ -0,0 +1,172 @@ +import * as StackBlur from './stackblur'; + +class BgImage { + constructor(){ + this.loaded = false; + + this.isCrosBlocked = false; + + this.domStyles = $('head').append('').find('style[data-type="bgimg-styles"]'); + this.posStyles = $('head').append('').find('style[data-type="bgimg-styles-pos"]'); + this.domStyleSheet = this.domStyles[0].sheet; + this.posStyleSheet = this.posStyles[0].sheet; + + this.hasBlurArea = false; + this.blurRadius = 20; + + this.blurSource = null; + + this.img = new Image(); + this.img.crossOrigin = 'anonymous' + this.img.addEventListener('load', this.onImageLoaded.bind(this)); + + this.imgRatio = 0; + + this.imgOffset = { + top: 0, + left: 0, + }; + + this.imgSize = { + width: 0, + height: 0, + }; + + this.viewports = []; + + window.addEventListener('resize', this.onWindowResize.bind(this)); + } + + async loadImage(src, xCenter, yCenter){ + try { + await fetch(src); + } catch(e){ + this.isCrosBlocked = true; + this.img.crossOrigin = null; + } + + this.source = src; + this.xCenter = xCenter || 50; + this.yCenter = yCenter || 50; + + this.img.src = src; + } + + generateBlurImage(){ + return new Promise((resolve, reject) => { + let canvas = document.createElement('canvas'); + canvas.width = this.img.width; + canvas.height = this.img.height; + let ctx = canvas.getContext('2d'); + ctx.drawImage(this.img, 0, 0); + StackBlur.canvasRGBA(canvas, 0, 0, canvas.width, canvas.height, this.blurRadius); + + canvas.toBlob((blob) => { + this.blurSource = URL.createObjectURL(blob); + resolve(); + }); + }); + } + + async onImageLoaded(){ + this.imgRatio = this.img.width / this.img.height; + + if(this.hasBlurArea){ + if(!this.isCrosBlocked){ + //生成模糊的图片 + //css会有白边,为了效果好只能用canvas生成了 + await this.generateBlurImage(); + } else { + this.blurSource = this.source; + } + } + + this.fixPosition(); + + if(!this.loaded){ + this.viewports.forEach((viewport) => { + viewport.css({ + backgroundImage: "url('" + this.source + "')", + backgroundAttachement: 'fixed', + opacity: 1, + }); + }); + + var css = ".has-bgimg .bgimg-background-blur::after { "; + css += "opacity: 1; background-image: url('" + this.blurSource + "'); "; + if(this.isCrosBlocked){ + css += "filter: blur(" + this.blurRadius + "px); "; + } + css += ";"; + this.domStyleSheet.insertRule(css, this.domStyleSheet.rules.length); + + this.loaded = true; + } + } + + onWindowResize(){ + if(this.loaded) this.fixPosition(); + } + + getOverflowSize(screenSize, imgSize, centerPoint){ + let centerPointPx = imgSize * centerPoint / 100; + let maxOverflow = imgSize - screenSize; + let absOffset = centerPointPx - (screenSize / 2); + return Math.max(0, Math.min(maxOverflow, absOffset)); + } + + fixPosition(){ + let screenRatio = window.innerWidth / window.innerHeight; + //计算出适合的大小以及offset + if(this.imgRatio > screenRatio){ //窄屏 + //图片高度 = 屏幕高度,横向靠近中心点 + this.imgSize.height = window.innerHeight; + this.imgSize.width = this.imgSize.height * this.imgRatio; + this.imgOffset.top = 0; + this.imgOffset.left = - this.getOverflowSize(window.innerWidth, this.imgSize.width, this.xCenter); + } else { //宽屏 + //图片宽度 = 屏幕宽度,纵向靠近中心点 + this.imgSize.width = window.innerWidth; + this.imgSize.height = this.imgSize.width / this.imgRatio; + this.imgOffset.left = 0; + this.imgOffset.top = - this.getOverflowSize(window.innerHeight, this.imgSize.height, this.yCenter); + } + this.fixViewportPosition(); + } + + fixViewportPosition(){ + this.viewports.forEach((viewport) => { + //设置viewport的style + viewport.css({ + backgroundSize: this.imgSize.width + 'px ' + this.imgSize.height + 'px', + backgroundPosition: this.imgOffset.left + 'px ' + this.imgOffset.top + 'px', + }); + }); + + if(this.hasBlurArea){ + //设置伪元素viewport的style + let css = ".has-bgimg .bgimg-background-blur::after { "; + css += 'background-size: ' + this.imgSize.width + 'px ' + this.imgSize.height + 'px; '; + css += 'background-position: ' + this.imgOffset.left + 'px ' + this.imgOffset.top + 'px; '; + css += '}'; + + this.posStyleSheet.insertRule(css, this.posStyleSheet.rules.length); + if(this.posStyleSheet.rules.length > 1) this.posStyleSheet.deleteRule(this.posStyleSheet.rules.length - 2); + } + } + + addViewport(element){ + element.css('opacity', 0); + this.viewports.push(element); + } + + addBlurBackground(element){ + element.addClass('bgimg-background-blur'); + this.hasBlurArea = true; + } +} + +if(!global.isekai){ + global.isekai = {}; +} +global.isekai.BgImage = BgImage; diff --git a/src/stackblur.js b/src/stackblur.js new file mode 100644 index 0000000..37fb866 --- /dev/null +++ b/src/stackblur.js @@ -0,0 +1,595 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = global || self, factory(global.StackBlur = {})); +}(this, (function (exports) { 'use strict'; + + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + /* eslint-disable no-bitwise, unicorn/prefer-query-selector */ + + /** + * StackBlur - a fast almost Gaussian Blur For Canvas + * + * In case you find this class useful - especially in commercial projects - + * I am not totally unhappy for a small donation to my PayPal account + * mario@quasimondo.de + * + * Or support me on flattr: + * {@link https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript}. + * + * @module StackBlur + * @author Mario Klingemann + * Contact: mario@quasimondo.com + * Website: {@link http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html} + * Twitter: @quasimondo + * + * @copyright (c) 2010 Mario Klingemann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + /* eslint-disable max-len */ + var mulTable = [512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259]; + var shgTable = [9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]; + /* eslint-enable max-len */ + + /** + * @param {string|HTMLImageElement} img + * @param {string|HTMLCanvasElement} canvas + * @param {Float} radius + * @param {boolean} blurAlphaChannel + * @returns {undefined} + */ + + function processImage(img, canvas, radius, blurAlphaChannel) { + if (typeof img === 'string') { + img = document.getElementById(img); + } + + if (!img || !('naturalWidth' in img)) { + return; + } + + var w = img.naturalWidth; + var h = img.naturalHeight; + + if (typeof canvas === 'string') { + canvas = document.getElementById(canvas); + } + + if (!canvas || !('getContext' in canvas)) { + return; + } + + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + canvas.width = w; + canvas.height = h; + var context = canvas.getContext('2d'); + context.clearRect(0, 0, w, h); + context.drawImage(img, 0, 0); + + if (isNaN(radius) || radius < 1) { + return; + } + + if (blurAlphaChannel) { + processCanvasRGBA(canvas, 0, 0, w, h, radius); + } else { + processCanvasRGB(canvas, 0, 0, w, h, radius); + } + } + /** + * @param {string|HTMLCanvasElement} canvas + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @throws {Error|TypeError} + * @returns {ImageData} See {@link https://html.spec.whatwg.org/multipage/canvas.html#imagedata} + */ + + + function getImageDataFromCanvas(canvas, topX, topY, width, height) { + if (typeof canvas === 'string') { + canvas = document.getElementById(canvas); + } + + if (!canvas || _typeof(canvas) !== 'object' || !('getContext' in canvas)) { + throw new TypeError('Expecting canvas with `getContext` method ' + 'in processCanvasRGB(A) calls!'); + } + + var context = canvas.getContext('2d'); + + try { + return context.getImageData(topX, topY, width, height); + } catch (e) { + throw new Error('unable to access image data: ' + e); + } + } + /** + * @param {HTMLCanvasElement} canvas + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {undefined} + */ + + + function processCanvasRGBA(canvas, topX, topY, width, height, radius) { + if (isNaN(radius) || radius < 1) { + return; + } + + radius |= 0; + var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height); + imageData = processImageDataRGBA(imageData, topX, topY, width, height, radius); + canvas.getContext('2d').putImageData(imageData, topX, topY); + } + /** + * @param {ImageData} imageData + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {ImageData} + */ + + + function processImageDataRGBA(imageData, topX, topY, width, height, radius) { + var pixels = imageData.data; + var x, y, i, p, yp, yi, yw, rSum, gSum, bSum, aSum, rOutSum, gOutSum, bOutSum, aOutSum, rInSum, gInSum, bInSum, aInSum, pr, pg, pb, pa, rbs; + var div = 2 * radius + 1; // const w4 = width << 2; + + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; + var stackStart = new BlurStack(); + var stack = stackStart; + var stackEnd; + + for (i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + + if (i === radiusPlus1) { + stackEnd = stack; + } + } + + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + yw = yi = 0; + var mulSum = mulTable[radius]; + var shgSum = shgTable[radius]; + + for (y = 0; y < height; y++) { + rInSum = gInSum = bInSum = aInSum = rSum = gSum = bSum = aSum = 0; + rOutSum = radiusPlus1 * (pr = pixels[yi]); + gOutSum = radiusPlus1 * (pg = pixels[yi + 1]); + bOutSum = radiusPlus1 * (pb = pixels[yi + 2]); + aOutSum = radiusPlus1 * (pa = pixels[yi + 3]); + rSum += sumFactor * pr; + gSum += sumFactor * pg; + bSum += sumFactor * pb; + aSum += sumFactor * pa; + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + for (i = 1; i < radiusPlus1; i++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[p + 1]) * rbs; + bSum += (stack.b = pb = pixels[p + 2]) * rbs; + aSum += (stack.a = pa = pixels[p + 3]) * rbs; + rInSum += pr; + gInSum += pg; + bInSum += pb; + aInSum += pa; + stack = stack.next; + } + + stackIn = stackStart; + stackOut = stackEnd; + + for (x = 0; x < width; x++) { + pixels[yi + 3] = pa = aSum * mulSum >> shgSum; + + if (pa !== 0) { + pa = 255 / pa; + pixels[yi] = (rSum * mulSum >> shgSum) * pa; + pixels[yi + 1] = (gSum * mulSum >> shgSum) * pa; + pixels[yi + 2] = (bSum * mulSum >> shgSum) * pa; + } else { + pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; + } + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + aSum -= aOutSum; + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + aOutSum -= stackIn.a; + p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; + rInSum += stackIn.r = pixels[p]; + gInSum += stackIn.g = pixels[p + 1]; + bInSum += stackIn.b = pixels[p + 2]; + aInSum += stackIn.a = pixels[p + 3]; + rSum += rInSum; + gSum += gInSum; + bSum += bInSum; + aSum += aInSum; + stackIn = stackIn.next; + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + aOutSum += pa = stackOut.a; + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + aInSum -= pa; + stackOut = stackOut.next; + yi += 4; + } + + yw += width; + } + + for (x = 0; x < width; x++) { + gInSum = bInSum = aInSum = rInSum = gSum = bSum = aSum = rSum = 0; + yi = x << 2; + rOutSum = radiusPlus1 * (pr = pixels[yi]); + gOutSum = radiusPlus1 * (pg = pixels[yi + 1]); + bOutSum = radiusPlus1 * (pb = pixels[yi + 2]); + aOutSum = radiusPlus1 * (pa = pixels[yi + 3]); + rSum += sumFactor * pr; + gSum += sumFactor * pg; + bSum += sumFactor * pb; + aSum += sumFactor * pa; + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + yp = width; + + for (i = 1; i <= radius; i++) { + yi = yp + x << 2; + rSum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[yi + 1]) * rbs; + bSum += (stack.b = pb = pixels[yi + 2]) * rbs; + aSum += (stack.a = pa = pixels[yi + 3]) * rbs; + rInSum += pr; + gInSum += pg; + bInSum += pb; + aInSum += pa; + stack = stack.next; + + if (i < heightMinus1) { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + + for (y = 0; y < height; y++) { + p = yi << 2; + pixels[p + 3] = pa = aSum * mulSum >> shgSum; + + if (pa > 0) { + pa = 255 / pa; + pixels[p] = (rSum * mulSum >> shgSum) * pa; + pixels[p + 1] = (gSum * mulSum >> shgSum) * pa; + pixels[p + 2] = (bSum * mulSum >> shgSum) * pa; + } else { + pixels[p] = pixels[p + 1] = pixels[p + 2] = 0; + } + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + aSum -= aOutSum; + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + aOutSum -= stackIn.a; + p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; + rSum += rInSum += stackIn.r = pixels[p]; + gSum += gInSum += stackIn.g = pixels[p + 1]; + bSum += bInSum += stackIn.b = pixels[p + 2]; + aSum += aInSum += stackIn.a = pixels[p + 3]; + stackIn = stackIn.next; + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + aOutSum += pa = stackOut.a; + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + aInSum -= pa; + stackOut = stackOut.next; + yi += width; + } + } + + return imageData; + } + /** + * @param {HTMLCanvasElement} canvas + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {undefined} + */ + + + function processCanvasRGB(canvas, topX, topY, width, height, radius) { + if (isNaN(radius) || radius < 1) { + return; + } + + radius |= 0; + var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height); + imageData = processImageDataRGB(imageData, topX, topY, width, height, radius); + canvas.getContext('2d').putImageData(imageData, topX, topY); + } + /** + * @param {ImageData} imageData + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {ImageData} + */ + + + function processImageDataRGB(imageData, topX, topY, width, height, radius) { + var pixels = imageData.data; + var x, y, i, p, yp, yi, yw, rSum, gSum, bSum, rOutSum, gOutSum, bOutSum, rInSum, gInSum, bInSum, pr, pg, pb, rbs; + var div = 2 * radius + 1; // const w4 = width << 2; + + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; + var stackStart = new BlurStack(); + var stack = stackStart; + var stackEnd; + + for (i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + + if (i === radiusPlus1) { + stackEnd = stack; + } + } + + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + yw = yi = 0; + var mulSum = mulTable[radius]; + var shgSum = shgTable[radius]; + + for (y = 0; y < height; y++) { + rInSum = gInSum = bInSum = rSum = gSum = bSum = 0; + rOutSum = radiusPlus1 * (pr = pixels[yi]); + gOutSum = radiusPlus1 * (pg = pixels[yi + 1]); + bOutSum = radiusPlus1 * (pb = pixels[yi + 2]); + rSum += sumFactor * pr; + gSum += sumFactor * pg; + bSum += sumFactor * pb; + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack = stack.next; + } + + for (i = 1; i < radiusPlus1; i++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[p + 1]) * rbs; + bSum += (stack.b = pb = pixels[p + 2]) * rbs; + rInSum += pr; + gInSum += pg; + bInSum += pb; + stack = stack.next; + } + + stackIn = stackStart; + stackOut = stackEnd; + + for (x = 0; x < width; x++) { + pixels[yi] = rSum * mulSum >> shgSum; + pixels[yi + 1] = gSum * mulSum >> shgSum; + pixels[yi + 2] = bSum * mulSum >> shgSum; + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; + rInSum += stackIn.r = pixels[p]; + gInSum += stackIn.g = pixels[p + 1]; + bInSum += stackIn.b = pixels[p + 2]; + rSum += rInSum; + gSum += gInSum; + bSum += bInSum; + stackIn = stackIn.next; + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + stackOut = stackOut.next; + yi += 4; + } + + yw += width; + } + + for (x = 0; x < width; x++) { + gInSum = bInSum = rInSum = gSum = bSum = rSum = 0; + yi = x << 2; + rOutSum = radiusPlus1 * (pr = pixels[yi]); + gOutSum = radiusPlus1 * (pg = pixels[yi + 1]); + bOutSum = radiusPlus1 * (pb = pixels[yi + 2]); + rSum += sumFactor * pr; + gSum += sumFactor * pg; + bSum += sumFactor * pb; + stack = stackStart; + + for (i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack = stack.next; + } + + yp = width; + + for (i = 1; i <= radius; i++) { + yi = yp + x << 2; + rSum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[yi + 1]) * rbs; + bSum += (stack.b = pb = pixels[yi + 2]) * rbs; + rInSum += pr; + gInSum += pg; + bInSum += pb; + stack = stack.next; + + if (i < heightMinus1) { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + + for (y = 0; y < height; y++) { + p = yi << 2; + pixels[p] = rSum * mulSum >> shgSum; + pixels[p + 1] = gSum * mulSum >> shgSum; + pixels[p + 2] = bSum * mulSum >> shgSum; + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; + rSum += rInSum += stackIn.r = pixels[p]; + gSum += gInSum += stackIn.g = pixels[p + 1]; + bSum += bInSum += stackIn.b = pixels[p + 2]; + stackIn = stackIn.next; + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + stackOut = stackOut.next; + yi += width; + } + } + + return imageData; + } + /** + * + */ + + + var BlurStack = + /** + * Set properties. + */ + function BlurStack() { + _classCallCheck(this, BlurStack); + + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.next = null; + }; + + exports.BlurStack = BlurStack; + exports.canvasRGB = processCanvasRGB; + exports.canvasRGBA = processCanvasRGBA; + exports.image = processImage; + exports.imageDataRGB = processImageDataRGB; + exports.imageDataRGBA = processImageDataRGBA; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})));