Paladox has uploaded a new change for review.
https://gerrit.wikimedia.org/r/227064
Change subject: Clean up Apex skin
......................................................................
Clean up Apex skin
* Cleans up code in apex skin to match some of vector which this skin is based
on.
Includes two new configs to enable or disable watch icon and the search
bar with an icon instead of button.
Adds two missing files.
Add search icon in svg format too.
Change-Id: If89ce311e26751511fc4285f3f836fed1a57a686
---
M ApexTemplate.php
M SkinApex.php
A csshover.htc
A csshover.min.htc
A hooks.txt
A i18n/pt.json
M i18n/qqq.json
R resources/images/icons/search-ltr.png
A resources/images/icons/search-ltr.svg
A resources/images/icons/search-rtl.png
A resources/images/icons/search-rtl.svg
M resources/screen-narrow.css
M resources/screen.css
M skin.json
14 files changed, 591 insertions(+), 148 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/skins/apex
refs/changes/64/227064/1
diff --git a/ApexTemplate.php b/ApexTemplate.php
index 23b673c..f8e19fc 100644
--- a/ApexTemplate.php
+++ b/ApexTemplate.php
@@ -16,12 +16,18 @@
// Build additional attributes for navigation urls
$nav = $this->data['content_navigation'];
- $mode = $this->getSkin()->getUser()->isWatched(
$this->getSkin()->getRelevantTitle() ) ?
- 'unwatch' : 'watch';
- if ( isset( $nav['actions'][$mode] ) ) {
- $nav['views'][$mode] = $nav['actions'][$mode];
- $nav['views'][$mode]['primary'] = true;
- unset( $nav['actions'][$mode] );
+
+ if ( $this->config->get( 'ApexUseIconWatch' ) ) {
+ $mode = $this->getSkin()->getUser()->isWatched(
$this->getSkin()->getRelevantTitle() )
+ ? 'unwatch'
+ : 'watch';
+
+ if ( isset( $nav['actions'][$mode] ) ) {
+ $nav['views'][$mode] = $nav['actions'][$mode];
+ $nav['views'][$mode]['class'] = rtrim( 'icon '
. $nav['views'][$mode]['class'], ' ' );
+ $nav['views'][$mode]['primary'] = true;
+ unset( $nav['actions'][$mode] );
+ }
}
// TODO: Move defining the logo path to a resource loader
module with media queries to add
@@ -69,47 +75,83 @@
$this->data['personal_urls'] =
array_reverse( $this->data['personal_urls'] );
}
+
+ $this->data['pageLanguage'] =
+
$this->getSkin()->getTitle()->getPageViewLanguage()->getHtmlCode();
+
// Output HTML Page
$this->html( 'headelement' );
-?>
+ ?>
<div class="apex-content-wrapper">
- <div id="content" class="mw-body">
+ <div id="content" class="mw-body" role="main">
<a id="top"></a>
- <div id="mw-js-message"
style="display:none;"<?php $this->html( 'userlangattributes' ) ?>></div>
- <?php if ( $this->data['sitenotice'] ): ?>
- <div id="siteNotice"><?php $this->html(
'sitenotice' ) ?></div>
- <?php endif; ?>
- <h1 id="firstHeading"
class="firstHeading"><?php $this->html( 'title' ) ?></h1>
- <div id="bodyContent">
- <?php if ( $this->data['isarticle'] ):
?>
- <div id="siteSub"><?php $this->msg(
'tagline' ) ?></div>
- <?php endif; ?>
- <div id="contentSub"<?php $this->html(
'userlangattributes' ) ?>><?php $this->html( 'subtitle' ) ?></div>
- <?php if ( $this->data['undelete'] ): ?>
- <div id="contentSub2"><?php
$this->html( 'undelete' ) ?></div>
- <?php endif; ?>
- <?php if( $this->data['newtalk'] ): ?>
- <div class="usermessage"><?php
$this->html( 'newtalk' ) ?></div>
- <?php endif; ?>
- <?php if ( $this->data['showjumplinks']
): ?>
+
+ <?php
+ if ( $this->data['sitenotice'] ) {
+ ?>
+ <div id="siteNotice"><?php $this->html(
'sitenotice' ) ?></div>
+ <?php
+ }
+ ?>
+ <?php
+ // Loose comparison with '!=' is intentional,
to catch null and false too, but not '0'
+ if ( $this->data['title'] != '' ) {
+ ?>
+ <h1 id="firstHeading" class="firstHeading"
lang="<?php $this->text( 'pageLanguage' ); ?>"><?php
+ $this->html( 'title' )
+ ?></h1>
+ <?php
+ } ?>
+ <?php $this->html( 'prebodyhtml' ) ?>
+ <div id="bodyContent" class="mw-body-content">
+ <?php
+ if ( $this->data['isarticle'] ) {
+ ?>
+ <div id="siteSub"><?php
$this->msg( 'tagline' ) ?></div>
+ <?php
+ }
+ ?>
+ <div id="contentSub"<?php $this->html(
'userlangattributes' ) ?>><?php
+ $this->html( 'subtitle' )
+ ?></div>
+ <?php
+ if ( $this->data['undelete'] ) {
+ ?>
+ <div id="contentSub2"><?php
$this->html( 'undelete' ) ?></div>
+ <?php
+ }
+ ?>
+ <?php
+ if ( $this->data['newtalk'] ) {
+ ?>
+ <div class="usermessage"><?php
$this->html( 'newtalk' ) ?></div>
+ <?php
+ }
+ ?>
<div id="jump-to-nav" class="mw-jump">
<?php $this->msg( 'jumpto' ) ?>
- <a href="#mw-head"><?php
$this->msg( 'jumptonavigation' ) ?></a><?php $this->msg( 'comma-separator' ) ?>
+ <a href="#mw-head"><?php
+ $this->msg(
'jumptonavigation' )
+ ?></a><?php $this->msg(
'comma-separator' ) ?>
<a href="#p-search"><?php
$this->msg( 'jumptosearch' ) ?></a>
</div>
- <?php endif; ?>
- <?php $this->html( 'bodycontent' ) ?>
- <?php if ( $this->data['printfooter']
): ?>
- <div class="printfooter">
- <?php $this->html( 'printfooter' ); ?>
- </div>
- <?php endif; ?>
- <?php if ( $this->data['catlinks'] ): ?>
- <?php $this->html( 'catlinks' ); ?>
- <?php endif; ?>
- <?php if (
$this->data['dataAfterContent'] ): ?>
- <?php $this->html( 'dataAfterContent'
); ?>
- <?php endif; ?>
+ <?php
+ $this->html( 'bodycontent' );
+ if ( $this->data['printfooter'] ) {
+ ?>
+ <div class="printfooter">
+ <?php $this->html(
'printfooter' ); ?>
+ </div>
+ <?php
+ }
+ if ( $this->data['catlinks'] ) {
+ $this->html( 'catlinks' );
+ }
+
+ if ( $this->data['dataAfterContent'] ) {
+ $this->html( 'dataAfterContent'
);
+ }
+ ?>
<div class="visualClear"></div>
<?php $this->html( 'debughtml' ); ?>
</div>
@@ -131,31 +173,47 @@
<div class="apex-flyout-pull"></div>
<?php $this->renderPortals( $this->data['sidebar'] ); ?>
</div>
- <div id="footer"<?php $this->html( 'userlangattributes' ) ?>>
- <?php foreach( $this->getFooterLinks() as $category =>
$links ): ?>
+ <div id="footer" role="contentinfo"<?php $this->html(
'userlangattributes' ) ?>>
+ <?php
+ foreach ( $this->getFooterLinks() as $category =>
$links ) {
+ ?>
<ul id="footer-<?php echo $category ?>">
- <?php foreach( $links as $link ): ?>
- <li id="footer-<?php echo
$category ?>-<?php echo $link ?>">
- <?php $this->html(
$link ) ?>
- </li>
- <?php endforeach; ?>
+ <?php
+ foreach ( $links as $link ) {
+ ?>
+ <li id="footer-<?php echo
$category ?>-<?php echo $link ?>"><?php $this->html( $link ) ?></li>
+ <?php
+ }
+ ?>
</ul>
- <?php endforeach; ?>
- <?php $footericons = $this->getFooterIcons("icononly");
- if ( count( $footericons ) > 0 ): ?>
+ <?php
+ }
+ ?>
+ <?php $footericons = $this->getFooterIcons( "icononly"
);
+ if ( count( $footericons ) > 0 ) {
+ ?>
<ul id="footer-icons" class="noprint">
- <?php foreach ( $footericons as
$blockName => $footerIcons ): ?>
- <li id="footer-<?php echo
htmlspecialchars( $blockName ); ?>ico">
- <?php foreach ( $footerIcons as
$icon ): ?>
- <?php echo
$this->getSkin()->makeFooterIcon( $icon ); ?>
- <?php endforeach; ?>
- </li>
- <?php endforeach; ?>
+ <?php
+ foreach ( $footericons as $blockName =>
$footerIcons ) {
+ ?>
+ <li id="footer-<?php echo
htmlspecialchars( $blockName ); ?>ico">
+ <?php
+ foreach ( $footerIcons
as $icon ) {
+ echo
$this->getSkin()->makeFooterIcon( $icon );
+ }
+ ?>
+ </li>
+ <?php
+ }
+ ?>
</ul>
- <?php endif; ?>
+ <?php
+ }
+ ?>
<div style="clear:both"></div>
</div>
<?php $this->printTrail(); ?>
+
</body>
</html>
<?php
@@ -164,7 +222,7 @@
/**
* Render a series of portals
*
- * @param $portals array
+ * @param array $portals
*/
protected function renderPortals( $portals ) {
// Force the rendering of the following portals
@@ -182,60 +240,74 @@
if ( $content === false ) {
continue;
}
- switch( $name ) {
+
+ // Numeric strings gets an integer when set as key,
cast back - T73639
+ $name = (string)$name;
+
+ switch ( $name ) {
case 'SEARCH':
break;
case 'TOOLBOX':
$this->renderPortal( 'tb',
$this->getToolbox(), 'toolbox', 'SkinTemplateToolboxEnd' );
break;
case 'LANGUAGES':
- if ( $this->data['language_urls'] ) {
+ if ( $this->data['language_urls'] !==
false ) {
$this->renderPortal( 'lang',
$this->data['language_urls'], 'otherlanguages' );
}
break;
default:
$this->renderPortal( $name, $content );
- break;
+ break;
}
}
}
/**
- * @param $name string
- * @param $content array
- * @param $msg null|string
- * @param $hook null|string|array
+ * @param string $name
+ * @param array $content
+ * @param null|string $msg
+ * @param null|string|array $hook
*/
protected function renderPortal( $name, $content, $msg = null, $hook =
null ) {
if ( $msg === null ) {
$msg = $name;
}
+ $msgObj = wfMessage( $msg );
+ $labelId = Sanitizer::escapeId( "p-$name-label" );
?>
-<div class="portal" id='<?php echo Sanitizer::escapeId( "p-$name" ) ?>'<?php
echo Linker::tooltip( 'p-' . $name ) ?>>
- <h5<?php $this->html( 'userlangattributes' ) ?>><?php $msgObj =
wfMessage( $msg ); echo htmlspecialchars( $msgObj->exists() ? $msgObj->text() :
$msg ); ?></h5>
- <div class="body">
-<?php
- if ( is_array( $content ) ): ?>
- <ul>
-<?php
- foreach( $content as $key => $val ): ?>
- <?php echo $this->makeListItem( $key, $val ); ?>
+ <div class="portal" role="navigation" id='<?php
+ echo Sanitizer::escapeId( "p-$name" )
+ ?>'<?php
+ echo Linker::tooltip( 'p-' . $name )
+ ?> aria-labelledby='<?php echo $labelId ?>'>
+ <h5<?php $this->html( 'userlangattributes' ) ?>
id='<?php echo $labelId ?>'><?php
+ echo htmlspecialchars( $msgObj->exists() ?
$msgObj->text() : $msg );
+ ?></h5>
-<?php
- endforeach;
- if ( $hook !== null ) {
- wfRunHooks( $hook, array( &$this, true ) );
- }
- ?>
- </ul>
-<?php
- else: ?>
- <?php echo $content; /* Allow raw HTML block to be defined by
extensions */ ?>
-<?php
- endif; ?>
- </div>
-</div>
-<?php
+ <div class="body">
+ <?php
+ if ( is_array( $content ) ) {
+ ?>
+ <ul>
+ <?php
+ foreach ( $content as $key =>
$val ) {
+ echo
$this->makeListItem( $key, $val );
+ }
+ if ( $hook !== null ) {
+ Hooks::run( $hook,
array( &$this, true ) );
+ }
+ ?>
+ </ul>
+ <?php
+ } else {
+ echo $content; /* Allow raw HTML block
to be defined by extensions */
+ }
+
+ $this->renderAfterPortlet( $name );
+ ?>
+ </div>
+ </div>
+ <?php
}
/**
@@ -342,19 +414,43 @@
<?php
break;
case 'SEARCH':
-?>
-<div id="p-search">
- <h5<?php $this->html( 'userlangattributes' ) ?>><label
for="searchInput"><?php $this->msg( 'search' ) ?></label></h5>
- <form action="<?php $this->text( 'wgScript' ) ?>" id="searchform">
- <div id="simpleSearch">
- <?php echo $this->makeSearchInput( array( 'id' =>
'searchInput', 'type' => 'text' ) ); ?>
- <?php echo $this->makeSearchButton( 'image', array(
'id' => 'searchButton', 'src' => $this->getSkin()->getSkinStylePath(
'images/icons/search.png' ), 'width' => '12', 'height' => '13' ) ); ?>
- <input type='hidden' name="title" value="<?php
$this->text( 'searchtitle' ) ?>"/>
- </div>
- </form>
-</div>
-<?php
- break;
+ ?>
+ <div id="p-search" role="search">
+ <h5<?php $this->html(
'userlangattributes' ) ?>>
+ <label
for="searchInput"><?php $this->msg( 'search' ) ?></label>
+ </h5>
+
+ <form action="<?php
$this->text( 'wgScript' ) ?>" id="searchform">
+ <div<?php echo
$this->config->get( 'ApexUseSimpleSearch' ) ? ' id="simpleSearch"' : '' ?>>
+ <?php
+ echo
$this->makeSearchInput( array( 'id' => 'searchInput' ) );
+ echo Html::hidden(
'title', $this->get( 'searchtitle' ) );
+ // We construct two
buttons (for 'go' and 'fulltext' search modes),
+ // but only one will be
visible and actionable at a time (they are
+ // overlaid on top of
each other in CSS).
+ // * Browsers will use
the 'fulltext' one by default (as it's the
+ // first in
tree-order), which is desirable when they are unable
+ // to show search
suggestions (either due to being broken or
+ // having JavaScript
turned off).
+ // * The
mediawiki.searchSuggest module, after doing tests for the
+ // broken browsers,
removes the 'fulltext' button and handles
+ // 'fulltext' search
itself; this will reveal the 'go' button and
+ // cause it to be
used.
+ echo
$this->makeSearchButton(
+ 'fulltext',
+ array( 'id' =>
'mw-searchButton', 'class' => 'searchButton mw-fallbackSearchButton' )
+ );
+ echo
$this->makeSearchButton(
+ 'go',
+ array( 'id' =>
'searchButton', 'class' => 'searchButton' )
+ );
+ ?>
+ </div>
+ </form>
+ </div>
+ <?php
+
+ break;
}
}
}
diff --git a/SkinApex.php b/SkinApex.php
index bcbd767..1b53bf2 100644
--- a/SkinApex.php
+++ b/SkinApex.php
@@ -8,25 +8,32 @@
protected static $bodyClasses = array( 'apex-animateLayout' );
- var $skinname = 'apex', $stylename = 'apex',
- $template = 'ApexTemplate', $useHeadElement = true;
+ public $skinname = 'apex';
+ public $stylename = 'apex';
+ public $template = 'ApexTemplate';
+ /**
+ * @var Config
+ */
+ private $apexConfig;
+
+ public function __construct() {
+ $this->apexConfig =
ConfigFactory::getDefaultInstance()->makeConfig( 'apex' );
+ }
/**
* Initializes output page and sets up skin-specific parameters
- * @param $out OutputPage object to initialize
+ * @param OutputPage $out Object to initialize
*/
public function initPage( OutputPage $out ) {
- global $wgLocalStylePath;
-
parent::initPage( $out );
// Append CSS which includes IE only behavior fixes for hover
support -
- // this is better than including this in a CSS fille since it
doesn't
+ // this is better than including this in a CSS file since it
doesn't
// wait for the CSS file to load before fetching the HTC file.
$min = $this->getRequest()->getFuzzyBool( 'debug' ) ? '' :
'.min';
$out->addHeadItem( 'csshover',
'<!--[if lt IE 7]><style
type="text/css">body{behavior:url("' .
- htmlspecialchars( $wgLocalStylePath ) .
+ htmlspecialchars( $this->getConfig()->get(
'LocalStylePath' ) ) .
"/{$this->stylename}/csshover{$min}.htc\")}</style><![endif]-->"
);
$out->addHeadItem( 'ie9-gradient',
@@ -34,20 +41,26 @@
'.gradient { filter: none;
}</style><![endif]-->'
);
- $out->addModuleScripts( 'skins.apex' );
+ $out->addModuleScripts( 'skins.apex.js' );
}
/**
- * Load skin and user CSS files in the correct order
- * fixes bug 22916
- * @param $out OutputPage object
+ * Loads skin and user CSS files.
+ * @param OutputPage $out
*/
- function setupSkinUserCss( OutputPage $out ){
+ function setupSkinUserCss( OutputPage $out ) {
parent::setupSkinUserCss( $out );
- $out->addModuleStyles( array(
- 'mediawiki.skinning.interface',
- 'skins.apex'
- ) );
+
+ $styles = array( 'mediawiki.skinning.interface', 'skins.apex' );
+ Hooks::run( 'SkinApexStyleModules', array( $this, &$styles ) );
+ $out->addModuleStyles( $styles );
+ }
+
+ /**
+ * Override to pass our Config instance to it
+ */
+ public function setupTemplate( $classname, $repository = false,
$cache_dir = false ) {
+ return new $classname( $this->apexConfig );
}
/**
diff --git a/csshover.htc b/csshover.htc
new file mode 100644
index 0000000..a13ea68
--- /dev/null
+++ b/csshover.htc
@@ -0,0 +1,284 @@
+<public:attach event="ondocumentready" onevent="CSSHover()" />
+<script>
+/**
+ * Whatever:hover - V3.11
+ * ------------------------------------------------------------
+ * Author - Peter Nederlof, http://www.xs4all.nl/~peterned
+ * License - http://creativecommons.org/licenses/LGPL/2.1
+ *
+ * Special thanks to Sergiu Dumitriu, http://purl.org/net/sergiu,
+ * for fixing the expression loop.
+ *
+ * Whatever:hover is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Whatever:hover is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * howto: body { behavior:url("csshover3.htc"); }
+ * ------------------------------------------------------------
+ */
+
+window.CSSHover = (function(){
+
+ // regular expressions, used and explained later on.
+ var REG_INTERACTIVE = /(^|\s)((([^a]([^ ]+)?)|(a([^#.][^
]+)+)):(hover|active|focus))/i;
+ var REG_AFFECTED = /(.*?)\:(hover|active|focus)/i;
+ var REG_PSEUDO = /[^:]+:([a-z\-]+).*/i;
+ var REG_SELECT = /(\.([a-z0-9_\-]+):[a-z]+)|(:[a-z]+)/gi;
+ var REG_CLASS = /\.([a-z0-9_\-]*on(hover|active|focus))/i;
+ var REG_MSIE = /msie (5|6|7)/i;
+ var REG_COMPAT = /backcompat/i;
+
+ // property mapping, real css properties must be used in order to clear
expressions later on...
+ // Uses obscure css properties that no-one is likely to use. The
properties are borrowed to
+ // set an expression, and are then restored to the most likely correct
value.
+ var Properties = {
+ index: 0,
+ list: ['text-kashida', 'text-kashida-space', 'text-justify'],
+ get: function() {
+ return this.list[(this.index++)%this.list.length];
+ }
+ };
+
+ // camelize is used to convert css properties from (eg) text-kashida to
textKashida
+ var camelize = function(str) {
+ return str.replace(/-(.)/mg, function(result, match){
+ return match.toUpperCase();
+ });
+ };
+
+ /**
+ * Local CSSHover object
+ * --------------------------
+ */
+
+ var CSSHover = {
+
+ // array of CSSHoverElements, used to unload created events
+ elements: [],
+
+ // buffer used for checking on duplicate expressions
+ callbacks: {},
+
+ // init, called once ondomcontentready via the exposed
window.CSSHover function
+ init:function() {
+ // don't run in IE8 standards; expressions don't work
in standards mode anyway,
+ // and the stuff we're trying to fix should already
work properly
+ if(!REG_MSIE.test(navigator.userAgent) &&
!REG_COMPAT.test(window.document.compatMode)) {
+ return;
+ }
+
+ // start parsing the existing stylesheets
+ var sheets = window.document.styleSheets, l =
sheets.length;
+ for(var i=0; i<l; i++) {
+ this.parseStylesheet(sheets[i]);
+ }
+ },
+
+ // called from init, parses individual stylesheets
+ parseStylesheet:function(sheet) {
+ // check sheet imports and parse those recursively
+ if(sheet.imports) {
+ try {
+ var imports = sheet.imports;
+ var l = imports.length;
+ for(var i=0; i<l; i++) {
+
this.parseStylesheet(sheet.imports[i]);
+ }
+ } catch(securityException){
+ // trycatch for various possible errors
+ }
+ }
+
+ // interate the sheet's rules and send them to the
parser
+ try {
+ var rules = sheet.rules;
+ var r = rules.length;
+ for(var j=0; j<r; j++) {
+ this.parseCSSRule(rules[j], sheet);
+ }
+ } catch(someException){
+ // trycatch for various errors, most likely
accessing the sheet's rules.
+ }
+ },
+
+ // magic starts here ...
+ parseCSSRule:function(rule, sheet) {
+
+ // The sheet is used to insert new rules into, this
must be the same sheet the rule
+ // came from, to ensure that relative paths keep
pointing to the right location.
+
+ // only parse a rule if it contains an interactive
pseudo.
+ var select = rule.selectorText;
+ if(REG_INTERACTIVE.test(select)) {
+ var style = rule.style.cssText;
+
+ // affected elements are found by truncating
the selector after the interactive pseudo,
+ // eg: "div li:hover" >> "div li"
+ var affected = REG_AFFECTED.exec(select)[1];
+
+ // that pseudo is needed for a classname, and
defines the type of interaction (focus, hover, active)
+ // eg: "li:hover" >> "onhover"
+ var pseudo = select.replace(REG_PSEUDO, 'on$1');
+
+ // the new selector is going to use that
classname in a new css rule,
+ // since IE6 doesn't support multiple
classnames, this is merged into one classname
+ // eg: "li:hover" >> "li.onhover",
"li.folder:hover" >> "li.folderonhover"
+ var newSelect = select.replace(REG_SELECT,
'.$2' + pseudo);
+
+ // the classname is needed for the events that
are going to be set on affected nodes
+ // eg: "li.folder:hover" >> "folderonhover"
+ var className = REG_CLASS.exec(newSelect)[1];
+
+ // no need to set the same callback more than
once when the same selector uses the same classname
+ var hash = affected + className;
+ if(!this.callbacks[hash]) {
+
+ // affected elements are given an
expression under a borrowed css property, because fake properties
+ // can't have their expressions
cleared. Different properties are used per pseudo, to avoid
+ // expressions from overwriting
eachother. The expression does a callback to CSSHover.patch,
+ // rerouted via the exposed
window.CSSHover function.
+ var property = Properties.get();
+ var atRuntime = camelize(property);
+
+ // because the expression is added to
the stylesheet, and styles are always applied to html that is
+ // dynamically added to the dom, the
expression will also trigger for those new elements (provided
+ // they are selected by the affected
selector).
+ sheet.addRule(affected, property +
':expression(CSSHover(this, "'+pseudo+'", "'+className+'", "'+atRuntime+'"))');
+
+ // hash it, so an identical
selector/class combo does not duplicate the expression
+ this.callbacks[hash] = true;
+ }
+
+ // duplicate expressions need not be set, but
the style could differ
+ sheet.addRule(newSelect, style);
+ }
+ },
+
+ // called via the expression, patches individual nodes
+ patch:function(node, type, className, property) {
+
+ // restores the borrowed css property to the value of
its immediate parent, clearing
+ // the expression so that it's not repeatedly called.
+ try {
+ var value =
node.parentNode.currentStyle[property];
+ node.style[property] = value;
+ } catch(e) {
+ // the above reset should never fail, but just
in case, clear the runtimeStyle if it does.
+ // this will also stop the expression.
+ node.runtimeStyle[property] = '';
+ }
+
+ // just to make sure, also keep track of patched
classnames locally on the node
+ if(!node.csshover) {
+ node.csshover = [];
+ }
+
+ // and check for it to prevent duplicate events with
the same classname from being set
+ if(!node.csshover[className]) {
+ node.csshover[className] = true;
+
+ // create an instance for the given type and
class
+ var element = new CSSHoverElement(node, type,
className);
+
+ // and store that instance for unloading later
on
+ this.elements.push(element);
+ }
+
+ // returns a dummy value to the expression
+ return type;
+ },
+
+ // unload stuff onbeforeunload
+ unload:function() {
+ try {
+
+ // remove events
+ var l = this.elements.length;
+ for(var i=0; i<l; i++) {
+ this.elements[i].unload();
+ }
+
+ // and set properties to null
+ this.elements = [];
+ this.callbacks = {};
+
+ } catch (e) {
+ }
+ }
+ };
+
+ /**
+ * CSSHoverElement
+ * --------------------------
+ */
+
+ // the event types associated with the interactive pseudos
+ var CSSEvents = {
+ onhover: { activator: 'onmouseenter', deactivator:
'onmouseleave' },
+ onactive: { activator: 'onmousedown', deactivator: 'onmouseup'
},
+ onfocus: { activator: 'onfocus', deactivator: 'onblur' }
+ };
+
+ // CSSHoverElement constructor, called via CSSHover.patch
+ function CSSHoverElement(node, type, className) {
+
+ // the CSSHoverElement patches individual nodes by manually
applying the events that should
+ // have fired by the css pseudoclasses, eg mouseenter and
mouseleave for :hover.
+
+ this.node = node;
+ this.type = type;
+ var replacer = new RegExp('(^|\\s)'+className+'(\\s|$)', 'g');
+
+ // store event handlers for removal onunload
+ this.activator = function(){ node.className += ' ' +
className; };
+ this.deactivator = function(){ node.className =
node.className.replace(replacer, ' '); };
+
+ // add the events
+ node.attachEvent(CSSEvents[type].activator, this.activator);
+ node.attachEvent(CSSEvents[type].deactivator, this.deactivator);
+ }
+
+ CSSHoverElement.prototype = {
+ // onbeforeunload, called via CSSHover.unload
+ unload:function() {
+
+ // remove events
+ this.node.detachEvent(CSSEvents[this.type].activator,
this.activator);
+ this.node.detachEvent(CSSEvents[this.type].deactivator,
this.deactivator);
+
+ // and set properties to null
+ this.activator = null;
+ this.deactivator = null;
+ this.node = null;
+ this.type = null;
+ }
+ };
+
+ // add the unload to the onbeforeunload event
+ window.attachEvent('onbeforeunload', function(){
+ CSSHover.unload();
+ });
+
+ /**
+ * Public hook
+ * --------------------------
+ */
+
+ return function(node, type, className, property) {
+ if(node) {
+ // called via the css expression; patches individual
nodes
+ return CSSHover.patch(node, type, className, property);
+ } else {
+ // called ondomcontentready via the public:attach node
+ CSSHover.init();
+ }
+ };
+
+})();
+</script>
\ No newline at end of file
diff --git a/csshover.min.htc b/csshover.min.htc
new file mode 100644
index 0000000..7e5c57b
--- /dev/null
+++ b/csshover.min.htc
@@ -0,0 +1,12 @@
+<public:attach event="ondocumentready" onevent="CSSHover()" />
+<script>
+/**
+ * Whatever:hover - V3.11
+ * http://www.xs4all.nl/~peterned/
+ *
+ * Copyright (c) 2009 Peter Nederlof
+ * Licensed under the LGPL license
+ * http://creativecommons.org/licenses/LGPL/2.1
+ */
+window.CSSHover=(function(){var m=/(^|\s)((([^a]([^ ]+)?)|(a([^#.][^
]+)+)):(hover|active|focus))/i;var n=/(.*?)\:(hover|active|focus)/i;var
o=/[^:]+:([a-z\-]+).*/i;var p=/(\.([a-z0-9_\-]+):[a-z]+)|(:[a-z]+)/gi;var
q=/\.([a-z0-9_\-]*on(hover|active|focus))/i;var s=/msie (5|6|7)/i;var
t=/backcompat/i;var
u={index:0,list:['text-kashida','text-kashida-space','text-justify'],get:function(){return
this.list[(this.index++)%this.list.length]}};var v=function(c){return
c.replace(/-(.)/mg,function(a,b){return b.toUpperCase()})};var
w={elements:[],callbacks:{},init:function(){if(!s.test(navigator.userAgent)&&!t.test(window.document.compatMode)){return}var
a=window.document.styleSheets,l=a.length;for(var
i=0;i<l;i++){this.parseStylesheet(a[i])}},parseStylesheet:function(a){if(a.imports){try{var
b=a.imports;var l=b.length;for(var
i=0;i<l;i++){this.parseStylesheet(a.imports[i])}}catch(securityException){}}try{var
c=a.rules;var r=c.length;for(var
j=0;j<r;j++){this.parseCSSRule(c[j],a)}}catch(someException){}},parseCSSRule:function(a,b){var
c=a.selectorText;if(m.test(c)){var d=a.style.cssText;var e=n.exec(c)[1];var
f=c.replace(o,'on$1');var g=c.replace(p,'.$2'+f);var h=q.exec(g)[1];var
i=e+h;if(!this.callbacks[i]){var j=u.get();var
k=v(j);b.addRule(e,j+':expression(CSSHover(this, "'+f+'", "'+h+'",
"'+k+'"))');this.callbacks[i]=true}b.addRule(g,d)}},patch:function(a,b,c,d){try{var
f=a.parentNode.currentStyle[d];a.style[d]=f}catch(e){a.runtimeStyle[d]=''}if(!a.csshover){a.csshover=[]}if(!a.csshover[c]){a.csshover[c]=true;var
g=new CSSHoverElement(a,b,c);this.elements.push(g)}return
b},unload:function(){try{var l=this.elements.length;for(var
i=0;i<l;i++){this.elements[i].unload()}this.elements=[];this.callbacks={}}catch(e){}}};var
x={onhover:{activator:'onmouseenter',deactivator:'onmouseleave'},onactive:{activator:'onmousedown',deactivator:'onmouseup'},onfocus:{activator:'onfocus',deactivator:'onblur'}};function
CSSHoverElement(a,b,c){this.node=a;this.type=b;var d=new
RegExp('(^|\\s)'+c+'(\\s|$)','g');this.activator=function(){a.className+='
'+c};this.deactivator=function(){a.className=a.className.replace(d,'
')};a.attachEvent(x[b].activator,this.activator);a.attachEvent(x[b].deactivator,this.deactivator)}CSSHoverElement.prototype={unload:function(){this.node.detachEvent(x[this.type].activator,this.activator);this.node.detachEvent(x[this.type].deactivator,this.deactivator);this.activator=null;this.deactivator=null;this.node=null;this.type=null}};window.attachEvent('onbeforeunload',function(){w.unload()});return
function(a,b,c,d){if(a){return w.patch(a,b,c,d)}else{w.init()}}})();
+</script>
diff --git a/hooks.txt b/hooks.txt
new file mode 100644
index 0000000..9cdb10e
--- /dev/null
+++ b/hooks.txt
@@ -0,0 +1,6 @@
+Hooks provided by the Apex skin.
+
+'SkinApexStyleModules': Called when defining the list of module styles to be
+loaded by the Apex skin.
+$skin: SkinApex object
+&$styles: Array of module names whose style will be loaded for the skin
diff --git a/i18n/pt.json b/i18n/pt.json
new file mode 100644
index 0000000..26a3c66
--- /dev/null
+++ b/i18n/pt.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vitorvicentevalente"
+ ]
+ },
+ "apex-desc": "Versão moderna do Vetor com novo visual e muitas
melhorias de usabilidade",
+ "apex-action-addsection": "Adicionar tópico",
+ "apex-action-delete": "Eliminar",
+ "apex-action-move": "Mover",
+ "apex-action-protect": "Proteger",
+ "apex-action-undelete": "Restaurar",
+ "apex-action-unprotect": "Alterar proteção",
+ "apex-view-create": "Criar",
+ "apex-view-edit": "Editar",
+ "apex-view-history": "Ver histórico",
+ "apex-view-view": "Ler",
+ "apex-view-viewsource": "Ver código-fonte"
+}
diff --git a/i18n/qqq.json b/i18n/qqq.json
index e509757..8806002 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -1,7 +1,5 @@
{
- "@metadata": {
- "authors": []
- },
+ "@metadata": [],
"skinname-apex": "{{optional}}",
"apex-desc":
"{{desc|what=skin|name=Apex|url=https://www.mediawiki.org/wiki/Skin:Apex}}",
"apex.css": "{{optional}}",
@@ -17,4 +15,4 @@
"apex-view-history": "Tab label in the Apex skin. See for example
{{canonicalurl:Main_Page|useskin=apex}}\n{{Identical|View history}}",
"apex-view-view": "Tab label in the Apex skin (verb). See for example
{{canonicalurl:Main_Page|useskin=apex}}.\n{{Identical|Read}}",
"apex-view-viewsource": "Tab label in the Apex skin.\n{{Identical|View
source}}"
-}
\ No newline at end of file
+}
diff --git a/images/icons/search.png b/resources/images/icons/search-ltr.png
similarity index 100%
rename from images/icons/search.png
rename to resources/images/icons/search-ltr.png
Binary files differ
diff --git a/resources/images/icons/search-ltr.svg
b/resources/images/icons/search-ltr.svg
new file mode 100644
index 0000000..0720f20
--- /dev/null
+++ b/resources/images/icons/search-ltr.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg"
width="12" height="13"><g stroke-width="2" stroke="#6c6c6c" fill="none"><path
d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>
\ No newline at end of file
diff --git a/resources/images/icons/search-rtl.png
b/resources/images/icons/search-rtl.png
new file mode 100644
index 0000000..c26c8d0
--- /dev/null
+++ b/resources/images/icons/search-rtl.png
Binary files differ
diff --git a/resources/images/icons/search-rtl.svg
b/resources/images/icons/search-rtl.svg
new file mode 100644
index 0000000..622d5f9
--- /dev/null
+++ b/resources/images/icons/search-rtl.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg"
width="12" height="13"><g stroke-width="2" stroke="#6c6c6c" fill="none"><path
d="M.71 11.71l4-4"/><circle cx="7" cy="5" r="4"/></g></svg>
\ No newline at end of file
diff --git a/resources/screen-narrow.css b/resources/screen-narrow.css
index 310550f..85ae647 100644
--- a/resources/screen-narrow.css
+++ b/resources/screen-narrow.css
@@ -47,7 +47,3 @@
#searchInput:focus {
width: 6em;
}
-
-div#mw-js-message {
- right: 1.5em;
-}
\ No newline at end of file
diff --git a/resources/screen.css b/resources/screen.css
index 8ce4d50..76ccfef 100644
--- a/resources/screen.css
+++ b/resources/screen.css
@@ -494,6 +494,34 @@
opacity: 1;
}
+div#simpleSearch,
+#searchButton,
+#mw-searchButton {
+ top: 0;
+ width: 1.65em;
+ height: 100%;
+ cursor: pointer;
+ /* Hide button text and replace it with the image. */
+ text-indent: -99999px;
+ /* Opera 12 on RTL flips the text in a funny way without this. */
+ /* @noflip */
+ direction: ltr;
+ white-space: nowrap;
+ overflow: hidden;
+ background-image: url('images/icons/search-ltr.png');
+ /* @embed */
+ background-image: -webkit-linear-gradient(transparent, transparent),
url('images/icons/search-ltr.svg');
+ /* @embed */
+ background-image: linear-gradient(transparent, transparent),
url('images/icons/search-ltr.svg');
+ background-position: center center;
+ background-repeat: no-repeat;
+}
+
+div#simpleSearch,
+#mw-searchButton {
+ z-index: 1;
+}
+
#ca-watch a,
#ca-unwatch a {
display: inline-block;
@@ -567,25 +595,6 @@
list-style: none;
margin: 0;
padding: 0.25em;
-}
-
-div#mw-js-message {
- background-color: #fff;
- font-size: 0.8em;
- position: absolute;
- margin: 0;
- padding: 1em 2em;
- right: 3em;
- top: 1.5em;
- width: 20em;
- z-index: 10000;
- border: solid 1px #d5eaed;
- -webkit-border-radius: 0.25em;
- -moz-border-radius: 0.25em;
- border-radius: 0.25em;
- -webkit-box-shadow: 0 0.125em 0.5em 0 rgba(0, 0, 0, 0.25);
- -moz-box-shadow: 0 0.125em 0.5em 0 rgba(0, 0, 0, 0.25);
- box-shadow: 0 0.125em 0.5em 0 rgba(0, 0, 0, 0.25);
}
/* Background images */
diff --git a/skin.json b/skin.json
index 4bc6316..0749a35 100644
--- a/skin.json
+++ b/skin.json
@@ -1,13 +1,16 @@
{
"name": "Apex",
+ "namemsg": "skinname-apex",
"author": [
"Trevor Parscal"
],
"url": "https://www.mediawiki.org/wiki/Skin:Apex",
"descriptionmsg": "apex-desc",
- "namemsg": "skinname-apex",
"license-name": "GPL-2.0+",
"type": "skin",
+ "ConfigRegistry": {
+ "apex": "GlobalVarConfig::newInstance"
+ },
"ValidSkinNames": {
"apex": "Apex"
},
@@ -25,7 +28,7 @@
},
"ResourceModules": {
"skins.apex": {
- "position": "bottom",
+ "position": "top",
"styles": {
"resources/screen.css": {
"media": "screen"
@@ -36,7 +39,10 @@
"resources/screen-wide.css": {
"media": "screen and (min-width: 982px)"
}
- },
+ }
+ },
+ "skins.apex.js": {
+ "position": "bottom",
"scripts": "resources/apex.js"
}
},
@@ -45,6 +51,8 @@
"remoteSkinPath": "apex"
},
"config": {
+ "ApexUseSimpleSearch": true,
+ "ApexUseIconWatch": true,
"ApexLogo": {
"1x": false,
"2x": false
--
To view, visit https://gerrit.wikimedia.org/r/227064
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: If89ce311e26751511fc4285f3f836fed1a57a686
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/skins/apex
Gerrit-Branch: master
Gerrit-Owner: Paladox <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits