Bartosz Dziewoński has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/92315


Change subject: jQuery.confirmable: New inline confirmation module [WIP]
......................................................................

jQuery.confirmable: New inline confirmation module [WIP]

$().confirmable() can be applied to any inline-block element. It will
cause it to expand into "Are you sure? [Yes] [No]" question and
buttons when clicked, where the buttons are clones of the original
element – [Yes] will carry out the default action (or a different one,
if specified), [No] will collapse the interface back.

Possible uses include:
* Confirmable "rollback" links
* Confirmable "unwatch" links on watchlists
* Confirmable "thank" links (Echo extension's ones)

Added a demo with possible uses on history and watchlist pages.
Included Hebrew messages courtesy of Moriel.

Change-Id: I2f6e0bd4f6f0a84e1a0d7193cde076738f3cdd25
---
A docs/uidesign/confirmable.html
M languages/messages/MessagesEn.php
M languages/messages/MessagesHe.php
M languages/messages/MessagesQqq.php
M maintenance/language/messages.inc
M resources/Resources.php
A resources/jquery/jquery.confirmable.css
A resources/jquery/jquery.confirmable.js
A resources/jquery/jquery.confirmable.mediawiki.js
9 files changed, 359 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/15/92315/1

diff --git a/docs/uidesign/confirmable.html b/docs/uidesign/confirmable.html
new file mode 100644
index 0000000..855cb77
--- /dev/null
+++ b/docs/uidesign/confirmable.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+       <meta charset="utf-8">
+       <!--
+       The jQuery.confirmable module uses some additional modules and files
+       for internationalization support. These are omitted here for simplicity.
+       -->
+       <script type="text/javascript" 
src="../../resources/jquery/jquery.js"></script>
+       <link rel="stylesheet" 
href="../../resources/jquery/jquery.confirmable.css">
+       <script type="text/javascript" 
src="../../resources/jquery/jquery.confirmable.js"></script>
+</head>
+<body style="font: small sans-serif;">
+       <h2>Introduction</h2>
+
+       <p>The jQuery.confirmable module provides a simple inline confirmation 
script for potentially destructive or uncancellable actions.</p>
+
+       <p>Possible uses include confirmable "rollback" links in histories, 
confirmable "unwatch" links on watchlists, or confirmable "thanks" links 
(provided by the Echo extension).</p>
+
+       <p>Shown below is a demo of how each of those could work on history and 
watchlist entries, in an LTR and RTL language.</p>
+
+       <h2>Examples</h2>
+
+       <h3>LTR (English)</h3>
+
+       <p>Watchlist:</p>
+
+       <ul lang="en" dir="ltr">
+               <li class="mw-line-even mw-changeslist-line-not-watched">
+                       (<a href="#">diff</a> | <a href="#">hist</a>)
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="mw-title"><a href="#" 
class="mw-changeslist-title">Example page</a></span>; <span 
class="mw-changeslist-date">13:38</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="mw-plusminus-neg">(-130)</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <a href="#" class="mw-userlink">Example user</a>
+                       <span class="mw-usertoollinks">(<a href="#">Talk</a> | 
<a href="#">contribs</a> | <a href="#">block</a>)</span>
+                       <span class="comment">(example edit)</span>
+                       <span class="mw-rollback-link">[<a 
href="https://www.mediawiki.org/wiki/Random_ideas_for_rollback_to_be_shelved_and_forgotten_about";>rollback</a>]</span>
+                       (<span class="mw-unwatch-link"><a 
href="#">unwatch</a></span>)
+               </li>
+       </ul>
+
+       <p>History:</p>
+
+       <ul lang="en" dir="ltr">
+               <li>
+                       <span class="mw-history-histlinks">(cur | <a 
href="#">prev</a>)</span>
+                       <input type="radio" style="visibility: hidden;" 
/><input type="radio" checked />
+                       <a href="#" class="mw-changeslist-date">13:38, 28 
October 2013</a>
+                       <span class='history-user'>
+                               <a href="#" class="mw-userlink">Example user</a>
+                               <span class="mw-usertoollinks">(<a 
href="#">Talk</a> | <a href="#">contribs</a> | <a href="#">block</a>)</span>
+                       </span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="history-size">(1,654 bytes)</span>
+                       <span class="mw-plusminus-neg">(-130)</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="comment">(example edit)</span>
+                       (<span class="mw-rollback-link"><a 
href="https://www.mediawiki.org/wiki/Random_ideas_for_rollback_to_be_shelved_and_forgotten_about";>rollback
 1 edit</a></span> | <span class="mw-history-undo"><a href="#">undo</a></span> 
| <span class="mw-thanks-thank-link"><a href="#">thank</a></span>)
+               </li>
+       </ul>
+
+       <script type="text/javascript">
+               $( 'ul[lang="en"] .mw-rollback-link a' )
+                       .confirmable({ i18n: { confirm: 'Are you sure you want 
to rollback?' } });
+               $( 'ul[lang="en"] .mw-unwatch-link a' )
+                       .confirmable({ handler: function(){ alert('Unwatched!') 
} });
+               $( 'ul[lang="en"] .mw-thanks-thank-link a' )
+                       .confirmable({ handler: function(){ alert('Thanked!') } 
});
+       </script>
+
+       <h3>RTL (Hebrew)</h3>
+       <!-- All of the Hebrew text below has been basically pulled out of my… 
hat. -->
+
+       <p>Watchlist:</p>
+
+       <ul lang="he" dir="rtl">
+               <li class="mw-line-even mw-changeslist-line-not-watched">
+                       (<a href="#">הבדל</a> | <a href="#">היסטוריה</a>)
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="mw-title"><a href="#" 
class="mw-changeslist-title">דף דוגמה</a></span>; <span 
class="mw-changeslist-date">13:38</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="mw-plusminus-neg">(-57)</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <a href="#" class="mw-userlink">דוגמא אדם</a>
+                       <span class="mw-usertoollinks">(<a href="#">שיחה</a> | 
<a href="#">תרומות</a> | <a href="#">חסימה</a>)</span>
+                       <span class="comment">(עריכה לדוגמה)</span>
+                       <span class="mw-rollback-link">[<a 
href="https://www.mediawiki.org/wiki/Random_ideas_for_rollback_to_be_shelved_and_forgotten_about";>שחזור</a>]</span>
+                       (<span class="mw-unwatch-link"><a href="#">הפסקת 
מעקב</a></span>)
+               </li>
+       </ul>
+
+       <p>History:</p>
+
+       <ul lang="he" dir="rtl">
+               <li>
+                       <span class="mw-history-histlinks">(נוכחית | <a 
href="#">קודמת</a>)</span>
+                       <input type="radio" style="visibility: hidden;" 
/><input type="radio" checked />
+                       <a href="#" class="mw-changeslist-date">23:41, 12 במאי 
2012</a>
+                       <span class='history-user'>
+                               <a href="#" class="mw-userlink">דוגמא אדם</a>
+                               <span class="mw-usertoollinks">(<a 
href="#">שיחה</a> | <a href="#">תרומות</a> | <a href="#">חסימה</a>)</span>
+                       </span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="history-size">(1,762 בתים)</span>
+                       <span class="mw-plusminus-neg">(-57)</span>
+                       <span class="mw-changeslist-separator">. .</span>
+                       <span class="comment">(עריכה לדוגמה)</span>
+                       (<span class="mw-rollback-link"><a 
href="https://www.mediawiki.org/wiki/Random_ideas_for_rollback_to_be_shelved_and_forgotten_about";>שחזור
 עריכה אחת</a></span> | <span class="mw-history-undo"><a 
href="#">ביטול</a></span> | <span class="mw-thanks-thank-link"><a 
href="#">תודה</a></span>)
+               </li>
+       </ul>
+
+       <script type="text/javascript">
+               var hebrewI18n = {
+                       confirm: 'האם ברצונך להמשיך?',
+                       yes: 'כן',
+                       no: 'לא',
+               }
+
+               $( 'ul[lang="he"] .mw-rollback-link a' )
+                       .confirmable({ i18n: $.extend( {}, hebrewI18n, { 
confirm: 'האם ברצונך לשחזר?' } ) });
+               $( 'ul[lang="he"] .mw-unwatch-link a' )
+                       .confirmable({ i18n: hebrewI18n, handler: function(){ 
alert('Unwatched!') } });
+               $( 'ul[lang="he"] .mw-thanks-thank-link a' )
+                       .confirmable({ i18n: hebrewI18n, handler: function(){ 
alert('Thanked!') } });
+       </script>
+       <style type="text/css">
+               /* This is normally handled by CSSJanus. */
+               ul[dir=rtl] .jquery-confirmable-button {
+                       margin-left: 0;
+                       margin-right: 1ex;
+               }
+       </style>
+</body>
+</html>
+
diff --git a/languages/messages/MessagesEn.php 
b/languages/messages/MessagesEn.php
index 6d68d36..7c68945 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -954,6 +954,9 @@
 'hidetoc'                      => 'hide',
 'collapsible-collapse'         => 'Collapse',
 'collapsible-expand'           => 'Expand',
+'confirmable-confirm'          => 'Are {{GENDER:$1|you}} sure?',
+'confirmable-yes'              => 'Yes',
+'confirmable-no'               => 'No',
 'thisisdeleted'                => 'View or restore $1?',
 'viewdeleted'                  => 'View $1?',
 'restorelink'                  => '{{PLURAL:$1|one deleted edit|$1 deleted 
edits}}',
diff --git a/languages/messages/MessagesHe.php 
b/languages/messages/MessagesHe.php
index ffa2ae2..259ad2d 100644
--- a/languages/messages/MessagesHe.php
+++ b/languages/messages/MessagesHe.php
@@ -655,6 +655,9 @@
 'hidetoc' => 'הסתרה',
 'collapsible-collapse' => 'הסתרה',
 'collapsible-expand' => 'הצגה',
+'confirmable-confirm' => 'האם {{GENDER:$1|ברצונך}} להמשיך?',
+'confirmable-yes' => 'כן',
+'confirmable-no' => 'לא',
 'thisisdeleted' => 'לשחזר או להציג $1?',
 'viewdeleted' => 'להציג $1?',
 'restorelink' => '{{PLURAL:$1|גרסה מחוקה אחת|$1 גרסאות מחוקות}}',
diff --git a/languages/messages/MessagesQqq.php 
b/languages/messages/MessagesQqq.php
index a8b66b2..b4ce02d 100644
--- a/languages/messages/MessagesQqq.php
+++ b/languages/messages/MessagesQqq.php
@@ -913,6 +913,24 @@
 
 See the following example:
 {{Identical|Expand}}',
+'confirmable-confirm' => 'Question asking the user to confirm a potentially 
uncancellable action.
+"Yes" and "No" buttons are displayed beside it.
+
+See also:
+* {{msg-mw|confirmable-yes}}
+* {{msg-mw|confirmable-no}}',
+'confirmable-yes' => '{{Doc-actionlink}}
+Text of a button that will confirm triggering of a potentially uncancellable 
action.
+
+See also:
+* {{msg-mw|confirmable-confirm}}
+* {{msg-mw|confirmable-no}}',
+'confirmable-no' => '{{Doc-actionlink}}
+Text of a button that will cancel triggering of a potentially uncancellable 
action.
+
+See also:
+* {{msg-mw|confirmable-confirm}}
+* {{msg-mw|confirmable-yes}}',
 'thisisdeleted' => 'Message shown on a deleted page when the user has the 
undelete right. Parameters:
 * $1 - a link to [[Special:Undelete]], with {{msg-mw|restorelink}} as the text
 See also:
diff --git a/maintenance/language/messages.inc 
b/maintenance/language/messages.inc
index a4fc922..c29453d 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -335,6 +335,9 @@
                'hidetoc',
                'collapsible-collapse',
                'collapsible-expand',
+               'confirmable-confirm',
+               'confirmable-yes',
+               'confirmable-no',
                'thisisdeleted',
                'viewdeleted',
                'restorelink',
diff --git a/resources/Resources.php b/resources/Resources.php
index 2c02de8..5b6453e 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -191,6 +191,19 @@
        'jquery.colorUtil' => array(
                'scripts' => 'resources/jquery/jquery.colorUtil.js',
        ),
+       'jquery.confirmable' => array(
+               'scripts' => array(
+                       'resources/jquery/jquery.confirmable.js',
+                       'resources/jquery/jquery.confirmable.mediawiki.js',
+               ),
+               'messages' => array(
+                       'confirmable-confirm',
+                       'confirmable-yes',
+                       'confirmable-no',
+               ),
+               'styles' => 'resources/jquery/jquery.confirmable.css',
+               'dependencies' => 'mediawiki.jqueryMsg',
+       ),
        'jquery.cookie' => array(
                'scripts' => 'resources/jquery/jquery.cookie.js',
                'targets' => array( 'desktop', 'mobile' ),
diff --git a/resources/jquery/jquery.confirmable.css 
b/resources/jquery/jquery.confirmable.css
new file mode 100644
index 0000000..de69072
--- /dev/null
+++ b/resources/jquery/jquery.confirmable.css
@@ -0,0 +1,28 @@
+.jquery-confirmable-button {
+       /* Automatically flipped */
+       margin-left: 1ex;
+}
+
+.jquery-confirmable-wrapper {
+       /* Line breaks within the interface text are unpleasant */
+       white-space: nowrap;
+       /* Hiding the original text when it slides to the left */
+       overflow: hidden;
+}
+
+.jquery-confirmable-wrapper,
+.jquery-confirmable-element,
+.jquery-confirmable-interface {
+       /* We need inline-block to be able to size the elements and calculate 
their dimensions */
+       display: inline-block;
+       /* inline-block elements in this context align to baseline by default */
+       vertical-align: bottom;
+}
+
+.jquery-confirmable-element {
+       transition: margin 250ms cubic-bezier(0.2, 0.8, 0.2, 0.8);
+}
+
+.jquery-confirmable-interface {
+       transition: width 250ms cubic-bezier(0.2, 0.8, 0.2, 0.8);
+}
diff --git a/resources/jquery/jquery.confirmable.js 
b/resources/jquery/jquery.confirmable.js
new file mode 100644
index 0000000..39682e6
--- /dev/null
+++ b/resources/jquery/jquery.confirmable.js
@@ -0,0 +1,145 @@
+/**
+ * jQuery confirmable plugin
+ *
+ * Calling .confirmable() on a clickable element (like <a> or <button>) will
+ * result in an additional inline confirmation step being shown before the
+ * default action is carried out on click.
+ *
+ * Calling .confirmable({ handler: function () { … } }) will fire the handler
+ * only after the confirmation step.
+ *
+ * The element will have class="jquery-confirmable-element" added to it when
+ * it's clicked for the first time, which among other things means
+ * white-space: nowrap; and display: inline-block;. If the computed values for
+ * it are different when you make it confirmable, you might encounter 
unexpected
+ * behavior.
+ *
+ * @author Bartosz "Matma Rex" Dziewoński, 2013
+ *
+ * @license MIT
+ */
+( function ( $ ) {
+       var identity = function ( data ) {
+               return data;
+       };
+
+       $.fn.confirmable = function ( options ) {
+               options = $.extend( true, {}, $.fn.confirmable.defaultOptions, 
options || {} );
+
+               this.on( options.events, function ( e ) {
+                       var $element, $text, $buttonYes, $buttonNo, $wrapper, 
$interface, $elementClone,
+                               interfaceWidth, elementWidth, rtl, 
positionOffscreen, positionRestore, sideMargin;
+
+                       $element = $( this );
+
+                       if ( $element.data( 'jquery-confirmable-button' ) ) {
+                               // We're running on a clone of this element 
that represents the 'Yes' or 'No' button.
+                               // (This should never happen for the 'No' case 
unless calling code does bad things.)
+                               return;
+                       }
+
+                       // Only prevent native event handling. Stopping other 
JavaScript event handlers
+                       // is impossible because they might have already run 
(we have no control over the order).
+                       e.preventDefault();
+
+                       rtl = $element.css( 'direction' ) === 'rtl';
+                       if ( rtl ) {
+                               positionOffscreen = { position: 'absolute', 
right: '-9999px' };
+                               positionRestore = { position: '', right: '' };
+                               sideMargin = 'marginRight';
+                       } else {
+                               positionOffscreen = { position: 'absolute', 
left: '-9999px' };
+                               positionRestore = { position: '', left: '' };
+                               sideMargin = 'marginLeft';
+                       }
+
+                       if ( $element.hasClass( 'jquery-confirmable-element' ) 
) {
+                               $wrapper = $element.closest( 
'.jquery-confirmable-wrapper' );
+                               $interface = $wrapper.find( 
'.jquery-confirmable-interface' );
+                               $text = $interface.find( 
'.jquery-confirmable-text' );
+                               $buttonYes = $interface.find( 
'.jquery-confirmable-button-yes' );
+                               $buttonNo = $interface.find( 
'.jquery-confirmable-button-no' );
+
+                               interfaceWidth = $interface.data( 
'jquery-confirmable-width' );
+                               elementWidth = $element.data( 
'jquery-confirmable-width' );
+                       } else {
+                               $elementClone = $element.clone( true );
+                               $element.addClass( 'jquery-confirmable-element' 
);
+
+                               elementWidth = $element.width();
+                               $element.data( 'jquery-confirmable-width', 
elementWidth );
+
+                               $wrapper = $( '<span>' )
+                                       .addClass( 'jquery-confirmable-wrapper' 
);
+                               $element.wrap( $wrapper );
+
+                               // Build the mini-dialog
+                               $text = $( '<span>' )
+                                       .addClass( 'jquery-confirmable-text' )
+                                       .text( options.i18n.confirm );
+
+                               // Clone original element along with event 
handlers to easily replicate its behavior.
+                               // We could fiddle with .trigger() etc., but 
that is troublesome especially since
+                               // Safari doesn't implement .click() on <a> 
links and jQuery follows suit.
+                               $buttonYes = $elementClone.clone( true )
+                                       .addClass( 'jquery-confirmable-button 
jquery-confirmable-button-yes' )
+                                       .data( 'jquery-confirmable-button', 
true )
+                                       .text( options.i18n.yes );
+                               if ( options.handler ) {
+                                       $buttonYes.on( options.events, 
options.handler );
+                               }
+                               $buttonYes = options.buttonCallback( 
$buttonYes, 'yes' );
+
+                               // Clone it without any events and prevent 
default action to represent the 'No' button.
+                               $buttonNo = $elementClone.clone( false )
+                                       .addClass( 'jquery-confirmable-button 
jquery-confirmable-button-no' )
+                                       .data( 'jquery-confirmable-button', 
true )
+                                       .text( options.i18n.no )
+                                       .on( options.events, function ( e ) {
+                                               $element.css( sideMargin, 0 );
+                                               $interface.css( 'width', 0 );
+                                               e.preventDefault();
+                                       } );
+                               $buttonNo = options.buttonCallback( $buttonNo, 
'no' );
+
+                               // Prevent memory leaks
+                               $elementClone.remove();
+
+                               $interface = $( '<span>' )
+                                       .addClass( 
'jquery-confirmable-interface' )
+                                       .append( $text, $buttonYes, $buttonNo );
+                               $interface = options.wrapperCallback( 
$interface );
+
+                               // Render offscreen to measure real width
+                               $interface.css( positionOffscreen );
+                               // Insert it in the correct place while we're 
at it
+                               $element.after( $interface );
+                               interfaceWidth = $interface.width();
+                               $interface.data( 'jquery-confirmable-width', 
interfaceWidth );
+                               $interface.css( positionRestore );
+
+                               // Hide to animate the transition later
+                               $interface.css( 'width', 0 );
+                       }
+
+                       // Hide element, show interface. This triggers both 
transitions.
+                       // In a timeout to trigger the 'width' transition.
+                       setTimeout( function () {
+                               $element.css( sideMargin, -elementWidth );
+                               $interface.css( 'width', interfaceWidth );
+                       }, 1 );
+               } );
+       };
+
+       $.fn.confirmable.defaultOptions = {
+               events: 'click',
+               wrapperCallback: identity,
+               buttonCallback: identity,
+               handler: null,
+               i18n: {
+                       confirm: 'Are you sure?',
+                       yes: 'Yes',
+                       no: 'No'
+               }
+       };
+}( jQuery ) );
diff --git a/resources/jquery/jquery.confirmable.mediawiki.js 
b/resources/jquery/jquery.confirmable.mediawiki.js
new file mode 100644
index 0000000..8802418
--- /dev/null
+++ b/resources/jquery/jquery.confirmable.mediawiki.js
@@ -0,0 +1,9 @@
+/* jQuery.confirmable itself is independent of MediaWiki.
+   This file serves to inject our localised messages into it. */
+( function ( mw , $ ) {
+       $.fn.confirmable.defaultOptions.i18n = {
+               confirm: mw.message( 'confirmable-confirm', mw.user ).text(),
+               yes: mw.message( 'confirmable-yes' ).text(),
+               no: mw.message( 'confirmable-no' ).text()
+       };
+}( mediaWiki, jQuery ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/92315
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I2f6e0bd4f6f0a84e1a0d7193cde076738f3cdd25
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Bartosz Dziewoński <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to