diff --git a/IsekaiWidgets.i18n.php b/IsekaiWidgets.i18n.php
new file mode 100644
index 0000000..d803955
--- /dev/null
+++ b/IsekaiWidgets.i18n.php
@@ -0,0 +1,7 @@
+ [
+ 'htmldetails' => [0, 'htmldetails'],
+ 'htmlsummary' => [0, 'htmlsummary'],
+ ],
+];
\ No newline at end of file
diff --git a/IsekaiWidgets.zip b/IsekaiWidgets.zip
deleted file mode 100644
index 25feedd..0000000
Binary files a/IsekaiWidgets.zip and /dev/null differ
diff --git a/extension.json b/extension.json
index 6a141a3..a188d6e 100644
--- a/extension.json
+++ b/extension.json
@@ -7,16 +7,23 @@
"descriptionmsg": "isekai-widgets-desc",
"license-name": "GPL-2.0-or-later",
"type": "parserhook",
+ "requires": {
+ "MediaWiki": ">= 1.34.0"
+ },
"MessagesDirs": {
"IsekaiWidgets": [
"i18n"
]
},
+ "ExtensionMessagesFiles": {
+ "IsekaiWidgetsMagic": "IsekaiWidgets.i18n.php"
+ },
"AutoloadNamespaces": {
"Isekai\\Widgets\\": "includes"
},
"Hooks": {
- "ParserFirstCallInit": "Isekai\\Widgets\\Widgets::onParserSetup"
+ "ParserFirstCallInit": "Isekai\\Widgets\\Widgets::onParserSetup",
+ "BeforePageDisplay": "Isekai\\Widgets\\Widgets::onLoad"
},
"ResourceModules": {
"ext.isekai.widgets.global": {
@@ -97,11 +104,33 @@
"desktop",
"mobile"
]
+ },
+ "ext.isekai.collapse": {
+ "scripts": [
+ "ext.isekai.collapse.js"
+ ],
+ "styles": [
+ "ext.isekai.collapse.less"
+ ],
+ "targets": [
+ "desktop",
+ "mobile"
+ ]
}
},
"ResourceFileModulePaths": {
"localBasePath": "modules",
"remoteExtPath": "IsekaiWidgets/modules"
},
- "manifest_version": 1
+ "attributes": {
+ "CodeMirror": {
+ "TagModes": {
+ "tilegroup": "text/mediawiki",
+ "exfont": "text/mediawiki",
+ "details": "text/mediawiki",
+ "summary": "text/mediawiki"
+ }
+ }
+ },
+ "manifest_version": 2
}
\ No newline at end of file
diff --git a/i18n/ja.json b/i18n/ja.json
index 1a537da..a2b8e81 100644
--- a/i18n/ja.json
+++ b/i18n/ja.json
@@ -13,5 +13,5 @@
"isekai-discover-loading": "読み込み中...",
"isekai-discover-change-btn": "変える",
"isekai-discover-readmore-btn": "開く",
- "isekai-discover-error-cannotload": "サーバからのページを読み取りに失敗しま。"
+ "isekai-discover-error-cannotload": "サーバからのページを読み取りに失敗しました。"
}
\ No newline at end of file
diff --git a/includes/ExtraFontWidget.php b/includes/ExtraFontWidget.php
index 3116ef0..f6ee07f 100644
--- a/includes/ExtraFontWidget.php
+++ b/includes/ExtraFontWidget.php
@@ -7,7 +7,7 @@ class ExtraFontWidget {
public static function create($text, $params, $parser, $frame){
$existsFonts = $parser->extIsekaiWidgetsCache->get('extraFonts', INF, []);
- $content = $text = $frame->expand($text);
+ $content = $text = $parser->recursiveTagParse($text, $frame);
if (!isset($params['name']) || empty($params['name'])) {
return '' . wfMessage('isekai-font-error-invalid-params')->parse() . '' . $content;
}
diff --git a/includes/FontFaceWidget.php b/includes/FontFaceWidget.php
index 385c221..4af1334 100644
--- a/includes/FontFaceWidget.php
+++ b/includes/FontFaceWidget.php
@@ -11,7 +11,7 @@ class FontFaceWidget {
* @param \Parser $parser
* @param \PPFrame $frame
*/
- public static function create($text, $params, $parser, $frame){
+ public static function create($text, $params, $parser, $frame) {
if (!isset($params['src']) || !isset($params['name']) ||
empty($params['src']) || empty($params['name'])) {
return '' . wfMessage('isekai-fontface-error-invalid-params')->parse() . '';
@@ -42,8 +42,8 @@ class FontFaceWidget {
$fontUrl = $file->getUrl();
$fontId = substr(Utils::safeBase64Encode(md5($fontName, true)), 0, 8);
- $css = "";
+ $css = "";
$existsFonts[$fontName] = $fontId;
$existsFonts = $parser->extIsekaiWidgetsCache->set('extraFonts', $existsFonts);
diff --git a/includes/Html5Widget.php b/includes/Html5Widget.php
new file mode 100644
index 0000000..b7712be
--- /dev/null
+++ b/includes/Html5Widget.php
@@ -0,0 +1,35 @@
+getOutput()->addModules('ext.isekai.collapse');
+ $allowedAttr = ['class'];
+ $htmlArgs = array_filter($args, function($k) use($allowedAttr) {
+ return in_array($k, $allowedAttr);
+ }, ARRAY_FILTER_USE_KEY);
+
+ $content = '';
+ if ($text) {
+ $content = $parser->recursiveTagParse($text, $frame);
+ }
+
+ return [Html::rawElement('details', $htmlArgs, $content), "markerType" => 'nowiki'];
+ }
+
+ public static function createSummary(string $text, array $args, \Parser $parser, \PPFrame $frame) {
+ $allowedAttr = ['class'];
+ $htmlArgs = array_filter($args, function($k) use($allowedAttr) {
+ return in_array($k, $allowedAttr);
+ }, ARRAY_FILTER_USE_KEY);
+
+ $content = '';
+ if ($text) {
+ $content = $parser->recursiveTagParse($text, $frame);
+ }
+
+ return [Html::rawElement('summary', $htmlArgs, $content), "markerType" => 'nowiki'];
+ }
+}
\ No newline at end of file
diff --git a/includes/TileWidget.php b/includes/TileWidget.php
index 32a0c67..f4b86f9 100644
--- a/includes/TileWidget.php
+++ b/includes/TileWidget.php
@@ -130,7 +130,7 @@ class TileWidget {
}
private function getImagesArgs(array &$element, array &$content){
- /*$service = MediaWikiServices::getInstance();
+ $service = MediaWikiServices::getInstance();
$this->images = [];
// 提取wikitext图片
preg_match_all('/\[\[(?
.+?:.+?)(\|.*?)?\]\]/', $this->content, $matches);
@@ -149,13 +149,14 @@ class TileWidget {
preg_match_all('/
.*?)".*?srcset="(?.*?)"[^\>]+>/', $this->content, $matches);
if (isset($matches['src']) && !empty($matches['src'])) {
$this->images = array_merge($this->images, $matches['src']);
- }*/
+ }
if(!empty($this->images)){
$element['data-effect'] = 'image-set';
foreach($this->images as $image){
$content[] = Html::element('img', [
'src' => $image,
+ 'style' => 'display: none'
]);
}
}
diff --git a/includes/Widgets.php b/includes/Widgets.php
index 84bc1df..416bd28 100644
--- a/includes/Widgets.php
+++ b/includes/Widgets.php
@@ -2,20 +2,32 @@
namespace Isekai\Widgets;
use MapCacheLRU;
+use Parser;
class Widgets {
+ /**
+ * @param \Parser $parser
+ */
public static function onParserSetup(&$parser){
- $parser->extIsekaiWidgetsCache = new MapCacheLRU( 100 ); // 100 is arbitrary
+ $parser->extIsekaiWidgetsCache = new MapCacheLRU( 100 ); // 100 is arbitrary
- $parser->setHook('createpage', CreatePageWidget::class . '::create');
- $parser->setHook('discoverbox', DiscoverWidget::class . '::create');
- $parser->setHook('previewcard', PreviewCardWidget::class . '::create');
+ $parser->setHook('createpage', [CreatePageWidget::class, 'create']);
+ $parser->setHook('discoverbox', [DiscoverWidget::class, 'create']);
+ $parser->setHook('previewcard', [PreviewCardWidget::class, 'create']);
- $parser->setHook('tile', TileWidget::class . '::create');
- $parser->setHook('tilegroup', TileGroupWidget::class . '::create');
+ $parser->setHook('tile', [TileWidget::class, 'create']);
+ $parser->setHook('tilegroup', [TileGroupWidget::class, 'create']);
- $parser->setHook('fontface', FontFaceWidget::class . '::create');
- $parser->setHook('exfont', ExtraFontWidget::class . '::create');
- return true;
+ $parser->setHook('fontface', [FontFaceWidget::class, 'create']);
+ $parser->setHook('exfont', [ExtraFontWidget::class, 'create']);
+
+ $parser->setHook('details', [Html5Widget::class, 'createDetails']);
+ $parser->setHook('summary', [Html5Widget::class, 'createSummary']);
+
+ return true;
+ }
+
+ public static function onLoad(\OutputPage $outputPage) {
+ $outputPage->addModuleStyles("ext.isekai.collapse");
}
}
\ No newline at end of file
diff --git a/modules/ext.isekai.collapse.js b/modules/ext.isekai.collapse.js
new file mode 100644
index 0000000..2ecd15f
--- /dev/null
+++ b/modules/ext.isekai.collapse.js
@@ -0,0 +1,33 @@
+(function($) {
+ $('.isekai-collapse').addClass('animate')
+ $('.isekai-collapse .isekai-collapse-title').on('click', '', function(e) {
+ e.preventDefault();
+ var titleElem = $(this);
+ var containerElem = titleElem.parent('.isekai-collapse');
+ var contentElem = containerElem.find('.isekai-collapse-content');
+ if (containerElem.prop('open')) { // 需要收起
+ var collapsedHeight = titleElem.outerHeight();
+ var expandedHeight = collapsedHeight + contentElem.outerHeight();
+ containerElem.css('height', expandedHeight);
+ console.log('expandedHeight', expandedHeight);
+ requestAnimationFrame(function() {
+ console.log('collapsedHeight', collapsedHeight);
+ containerElem.addClass('closing').css('height', collapsedHeight);
+ setTimeout(function() {
+ containerElem.prop('open', false).removeClass('closing'); //.css('height', 'auto');
+ }, 260);
+ });
+ } else { // 需要展开
+ containerElem.prop('open', true);
+ var collapsedHeight = titleElem.outerHeight();
+ containerElem.css('height', collapsedHeight);
+ requestAnimationFrame(function() {
+ var expandedHeight = collapsedHeight + contentElem.outerHeight();
+ containerElem.css('height', expandedHeight);
+ /*setTimeout(function() {
+ containerElem.css('height', 'auto');
+ }, 260);*/
+ });
+ }
+ });
+})(jQuery);
diff --git a/modules/ext.isekai.collapse.less b/modules/ext.isekai.collapse.less
new file mode 100644
index 0000000..e303396
--- /dev/null
+++ b/modules/ext.isekai.collapse.less
@@ -0,0 +1,64 @@
+.isekai-collapse {
+ width: 50%;
+ background: #fff;
+ margin-bottom: .5rem;
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ border-radius: 5px;
+ overflow: hidden;
+
+ @media screen and (max-width: 767px) {
+ width: 100%;
+ }
+
+ &.animate {
+ overflow-y: hidden;
+ will-change: height;
+ transition: height 250ms ease-in-out;
+ }
+
+ .isekai-collapse-title {
+ padding: 1rem;
+ display: block;
+ background-color: #f7f7f7;
+ padding-left: 2.2rem;
+ position: relative;
+ cursor: pointer;
+ color: black;
+ font-size: 1rem;
+
+ &::before {
+ content: '';
+ border-width: 0.4rem;
+ border-style: solid;
+ border-color: transparent transparent transparent #000;
+ position: absolute;
+ top: 1.32rem;
+ left: 1.2rem;
+ transform: rotate(0);
+ transform-origin: 0.2rem 50%;
+ will-change: transform;
+ transition: transform 250ms ease;
+ }
+
+ &::-webkit-details-marker {
+ transform: rotate(90deg);
+ }
+ }
+
+ .isekai-collapse-content {
+ padding: 1em;
+ }
+
+ &[open] > .isekai-collapse-title:before {
+ transform: rotate(90deg);
+ }
+
+ &.closing[open] > .isekai-collapse-title:before {
+ transform: rotate(0);
+ }
+}
+
+.isekai-indent > .isekai-collapse {
+ padding-left: 0;
+ margin-left: 8px;
+}
\ No newline at end of file
diff --git a/modules/tile/tile.js b/modules/tile/tile.js
index dd0fa2b..3d9790b 100644
--- a/modules/tile/tile.js
+++ b/modules/tile/tile.js
@@ -519,7 +519,7 @@ isekai.tile.init = function () {
this._createTile = function(){
function switchImage(el, img_src, i){
- $.setTimeout(function(){
+ setTimeout(function(){
el.fadeOut(500, function(){
el.css("background-image", "url(" + img_src + ")");
el.fadeIn();
@@ -582,7 +582,11 @@ isekai.tile.init = function () {
element.addClass("image-set");
$.each(element.children("img"), function(){
- that.images.push(this);
+ var imgElem = document.createElement('img');
+ imgElem.src = this.src;
+ imgElem.srcset = this.srcset;
+ imgElem.alt = this.alt;
+ that.images.push(imgElem);
$(this).remove();
});
@@ -592,12 +596,14 @@ isekai.tile.init = function () {
var rnd_index = rand(0, temp.length - 1);
var div = $("").addClass("img -js-img-"+i).css("background-image", "url("+temp[rnd_index].src+")");
element.prepend(div);
- temp.splice(rnd_index, 1);
+ if (temp.length > 1) {
+ temp.splice(rnd_index, 1);
+ }
}
var a = [0, 1, 4, 3, 2];
- $.setInterval(function(){
+ setInterval(function(){
var temp = that.images.slice();
var colors = Colors.colors(Colors.PALETTES.ALL), bg;
bg = colors[rand(0, colors.length - 1)];
@@ -608,7 +614,9 @@ isekai.tile.init = function () {
var rnd_index = rand(0, temp.length - 1);
var div = element.find(".-js-img-"+a[i]);
switchImage(div, temp[rnd_index].src, i);
- temp.splice(rnd_index, 1);
+ if (temp.length > 1) {
+ temp.splice(rnd_index, 1);
+ }
}
a = a.reverse();
@@ -619,7 +627,7 @@ isekai.tile.init = function () {
this._runEffects = function(){
var o = this.options;
- if (this.effectInterval === false) this.effectInterval = $.setInterval(function(){
+ if (this.effectInterval === false) this.effectInterval = setInterval(function(){
var current, next;
current = $(this.slides[this.currentSlide]);
@@ -641,7 +649,7 @@ isekai.tile.init = function () {
};
this._stopEffects = function(){
- $.clearInterval(this.effectInterval);
+ clearInterval(this.effectInterval);
this.effectInterval = false;
};