http://www.mediawiki.org/wiki/Special:Code/MediaWiki/88986

Revision: 88986
Author:   salvatoreingala
Date:     2011-05-27 18:13:15 +0000 (Fri, 27 May 2011)
Log Message:
-----------
- Completed a simple prototype with most features
- This is incomplete, buggy and inefficient; only intended as a
  concept to check architectural feasibility
- Most incomplete/bad things should be marked with TODOs, though

Modified Paths:
--------------
    branches/salvatoreingala/Gadgets/Gadgets.i18n.php
    branches/salvatoreingala/Gadgets/Gadgets.php
    branches/salvatoreingala/Gadgets/GadgetsAjax.php
    branches/salvatoreingala/Gadgets/Gadgets_body.php
    branches/salvatoreingala/Gadgets/Gadgets_tests.php
    branches/salvatoreingala/Gadgets/modules/ext.gadgets.preferences.js

Property Changed:
----------------
    branches/salvatoreingala/Gadgets/
    branches/salvatoreingala/Gadgets/Gadgets_body.php


Property changes on: branches/salvatoreingala/Gadgets
___________________________________________________________________
Added: svn:mergeinfo
   + /trunk/extensions/Gadgets:88253-88717

Modified: branches/salvatoreingala/Gadgets/Gadgets.i18n.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets.i18n.php   2011-05-27 17:59:32 UTC 
(rev 88985)
+++ branches/salvatoreingala/Gadgets/Gadgets.i18n.php   2011-05-27 18:13:15 UTC 
(rev 88986)
@@ -917,6 +917,8 @@
        'gadgets-pagetext' => 'Lischt vu spezielle Gadgets, wu fir jede 
Benutzer in syyne [[Special:Preferences|persenlige Yystellige]] verfiegbar sin, 
wie s [[MediaWiki:Gadgets-definition|definiert]] isch.
 Die Ibersicht bietet e direkte Zuegang zue dr Syschtemnochrichte, wu d 
Bschryybig un dr Programmcode vu jedem Gadget din sin.',
        'gadgets-uses' => 'Bruucht',
+       'gadgets-required-rights' => 'Brucht {{PLURAL:$2|des Rächt:|die 
Rächt:}} $1',
+       'gadgets-default' => 'Fir alli standardmäßig aktiviert.',
        'gadgets-export' => 'Exportiere',
        'gadgets-export-title' => 'Hälferli exportiere',
        'gadgets-not-found' => 'Hälferli „$1“ isch nit gfunde wore.',
@@ -1752,6 +1754,7 @@
        'gadgets-title' => 'Tilleggsfunksjoner',
        'gadgets-pagetext' => 'Nedenfor er en liste over tilleggsfunksjoner 
brukere kan slå på i [[Special:Preferences|innstillingene]], som definert på 
[[MediaWiki:Gadgets-definition]]. Denne oversikten gir lett tilgang til 
systembeskjedsidene som definerer hvert verktøys beskrivelse og kode.',
        'gadgets-uses' => 'Bruk',
+       'gadgets-required-rights' => 'Krever {{PLURAL:$2|$1 rettighet|følgende 
rettigheter: $1}}.',
        'gadgets-default' => 'Aktivert for alle som standard',
        'gadgets-export' => 'Eksporter',
        'gadgets-export-title' => 'Tilleggsfunksjon eksport',
@@ -2067,6 +2070,7 @@
 Этот список позволяет легко получить доступ к страницам системных сообщений, 
определяющих описания и исходные коды гаджетов.',
        'gadgets-uses' => 'Туһанар',
        'gadgets-required-rights' => '$2 бэйэбил (быраап) ирдэнэр: «$1»',
+       'gadgets-default' => 'Барыларыгар холбоно сылдьар.',
        'gadgets-export' => 'Экспортаа',
        'gadgets-export-title' => 'Ҕааддьыты таһаарыы (экспорт)',
        'gadgets-not-found' => '"$1" ҕааддьыт көстүбэтэ.',
@@ -2287,6 +2291,8 @@
        'gadgets-pagetext' => 'Härunder finns en lista över finesser som 
användare kan aktivera i sina [[Special:Preferences|inställningar]], definierad 
av [[MediaWiki:Gadgets-definition|definieringarna]].
 Den här översikten ger enkel åtkomst till de systemmeddelanden som definierar 
beskrivningarna och koden för varje finess.',
        'gadgets-uses' => 'Använder',
+       'gadgets-required-rights' => 'Kräver {{PLURAL:$2|$1 rättighet|följande 
rättigheter: $1}}.',
+       'gadgets-default' => 'Som standard aktiverat för alla.',
        'gadgets-export' => 'Exportera',
        'gadgets-export-title' => 'Exportera finess',
        'gadgets-not-found' => 'Tillägg "$1" hittades inte.',

Modified: branches/salvatoreingala/Gadgets/Gadgets.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets.php        2011-05-27 17:59:32 UTC 
(rev 88985)
+++ branches/salvatoreingala/Gadgets/Gadgets.php        2011-05-27 18:13:15 UTC 
(rev 88986)
@@ -34,6 +34,7 @@
 $wgHooks['GetPreferences'][]                = 'GadgetHooks::getPreferences';
 $wgHooks['ResourceLoaderRegisterModules'][] = 'GadgetHooks::registerModules';
 $wgHooks['UnitTestsList'][]                 = 'GadgetHooks::unitTestsList';
+$wgHooks['UserLoadOptions'][]               = 'GadgetHooks::userLoadOptions';
 
 $dir = dirname(__FILE__) . '/';
 $wgExtensionMessagesFiles['Gadgets'] = $dir . 'Gadgets.i18n.php';
@@ -45,8 +46,8 @@
 $wgAutoloadClasses['GadgetHooks'] = $dir . 'Gadgets_body.php';
 $wgAutoloadClasses['GadgetResourceLoaderModule'] = $dir . 'Gadgets_body.php';
 $wgAutoloadClasses['SpecialGadgets'] = $dir . 'SpecialGadgets.php';
+$wgAutoloadClasses['GadgetsGlobalModule'] = $dir . 'Gadgets_body.php';
 $wgAutoloadClasses['GadgetsAjax'] = $dir . 'GadgetsAjax.php';
-$wgAutoloadClasses['GadgetsSpecialPreferencesTweaksModule'] = $dir . 
'GadgetsAjax.php';
 
 $wgSpecialPages['Gadgets'] = 'SpecialGadgets';
 $wgSpecialPageGroups['Gadgets'] = 'wiki';
@@ -57,6 +58,13 @@
 $wgAjaxExportList[] = 'GadgetsAjax::getUI';
 $wgAjaxExportList[] = 'GadgetsAjax::setPreferences';
 
+$wgResourceModules['ext.gadgets'] = array(
+       'class'                 => 'GadgetsGlobalModule'
+);
+
 $wgResourceModules['ext.gadgets.preferences'] = array(
-       'class'                 => 'GadgetsSpecialPreferencesTweaksModule'
+       'scripts'               => array( 'ext.gadgets.preferences.js' ),
+       'dependencies'  => array( 'jquery', 'jquery.json', 'jquery.ui.dialog', 
'mediawiki.htmlform', 'ext.gadgets' ),
+       'localBasePath' => $dir . 'modules/',
+       'remoteExtPath' => 'Gadgets/modules'
 );

Modified: branches/salvatoreingala/Gadgets/GadgetsAjax.php
===================================================================
--- branches/salvatoreingala/Gadgets/GadgetsAjax.php    2011-05-27 17:59:32 UTC 
(rev 88985)
+++ branches/salvatoreingala/Gadgets/GadgetsAjax.php    2011-05-27 18:13:15 UTC 
(rev 88986)
@@ -1,24 +1,39 @@
 <?php
 
-class GadgetsAjax {    
-       public static function getUI( /*$args...*/ ) {
-               global $wgUser;
-               
-               if ( $wgUser->isAnon() ) {
+class GadgetsAjax {
+       
+       //Common validation code
+       //Checks if the user is logged and check params syntax
+       //returns error string if vaildation is failed, true otherwise
+       private static function validateSyntax( $args ) {
+               $user = RequestContext::getMain()->getUser();
+               if ( $user->isAnon() ) {
                        return '<err#>' . wfMsgExt( 'gadgets-ajax-notallowed', 
'parseinline' );
                }
-               
-               //params are in the format "param|val"
-               $args = func_get_args();
 
+               //checks if all params are of the form 'param|value'
                foreach ( $args as $arg ) {
                        $set = explode( '|', $arg, 2 );
                        if ( count( $set ) != 2 ) {
                                return '<err#>' . wfMsgExt( 
'gadgets-ajax-wrongparams', 'parseinline' );
                        }
+               }
+               
+               return true;
+       }
+       
+       public static function getUI( /*$args...*/ ) {
+               //params are in the format "param|val"
+               $args = func_get_args();
+
+               $res = self::validateSyntax( $args );
+               if ( $res !== true ) {
+                       return $res;
+               }
+
+               foreach ( $args as $arg ) {
+                       list( $par, $val ) = explode( '|', $arg, 2 );;
                        
-                       list( $par, $val ) = $set;
-                       
                        switch( $par ) {
                                case "gadget":
                                        $gadget = $val;
@@ -49,14 +64,72 @@
                //TODO: options of "select" and similar fields cannot be passed 
as messages
                $form = new HTMLForm( $prefs, RequestContext::getMain() );
                
-               $form->mFieldData = Gadget::getUserPrefs( $wgUser, $gadget );
+               $user = RequestContext::getMain()->getUser();
+               
+               $form->mFieldData = Gadget::getUserPrefs( $user, $gadget );
 
-               //TODO: HTMLForm::getBody is not meant to be public, a 
refactoring is needed            
+               //TODO: HTMLForm::getBody is not meant to be public, a 
refactoring is needed
+               //      (or a completely different solution)
                return $form->getBody();
        }
        
        public static function setPreferences( /* args */ ) {
-               //TODO
-               throw new MWException( 'Not implemented' );
+               //TODO: should probably add tokens
+               
+               //params are in the format "param|val"
+               $args = func_get_args();
+
+               $res = self::validateSyntax( $args );
+               if ( $res !== true ) {
+                       return $res;
+               }
+
+               foreach ( $args as $arg ) {
+                       list( $par, $val ) = explode( '|', $arg, 2 );;
+                       
+                       switch( $par ) {
+                               case "gadget":
+                                       $gadget = $val;
+                                       break;
+                               case "json":
+                                       $json = $val;
+                                       break;
+                               default:
+                                       return '<err#>' . wfMsgExt( 
'gadgets-ajax-wrongparams', 'parseinline' );
+                       }
+               }
+
+               if ( !isset( $gadget ) || !isset( $json ) ) {
+                       return '<err#>' . wfMsgExt( 'gadgets-ajax-wrongparams', 
'parseinline' );
+               }
+               
+               $prefsDescriptionJson = Gadget::getGadgetPrefsDescription( 
$gadget );
+               $prefsDescription = FormatJson::decode( $prefsDescriptionJson, 
true );
+               
+               if ( $prefsDescription === null ) {
+                       //either the gadget doesn't exists or it exists but it 
has no prefs
+                       return '<err#>' . wfMsgExt( 'gadgets-ajax-wrongparams', 
'parseinline' );
+               }
+               
+               $userPrefs = FormatJson::decode( $json, true );
+               
+               if ( $userPrefs === null ) {
+                       return '<err#>' . wfMsgExt( 'gadgets-ajax-wrongparams', 
'parseinline' );
+               }
+               
+               foreach ( $userPrefs as $pref => $value ) {
+                       if ( !isset( $prefsDescription[$pref] ) ){
+                               //Nonexisting configuration parameter; ignore it
+                               unset( $userPrefs[$pref] );
+                       } else {
+                               //TODO: convert values to proper type, check 
coherency with specification
+                               //      and fix fields that don't pass 
validation
+                       }
+               }
+
+               $user = RequestContext::getMain()->getUser();
+               Gadget::setUserPrefs( $user, $gadget, $userPrefs );
+               
+               return 'true';
        }
 }

Modified: branches/salvatoreingala/Gadgets/Gadgets_body.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets_body.php   2011-05-27 17:59:32 UTC 
(rev 88985)
+++ branches/salvatoreingala/Gadgets/Gadgets_body.php   2011-05-27 18:13:15 UTC 
(rev 88986)
@@ -155,6 +155,24 @@
        }
 
        /**
+        * UserLoadOptions hook handler.
+        * @param $user
+        * @param &$options 
+        */
+       public static function userLoadOptions( $user, &$options ) {
+               //Remove gadget-*-config options, since they must not be 
delivered like other user preferences
+               foreach ( $options as $option => $value ){
+                       //TODO: Regexsp not coherent with current gadget's 
naming rules
+                       if ( preg_match( 
'/gadget-[a-zA-Z][a-zA-Z0-9_]*-config/', $option ) ) {
+                               //TODO: cache them before unsetting
+                               unset( $options[$option] );
+                       }
+               }
+               return true;
+       }
+
+
+       /**
         * Adds one legacy script to output.
         * 
         * @param $page String: Unprefixed page title
@@ -548,6 +566,8 @@
        }
        
        
+       //TODO: put the following static methods somewhere else
+       
        public static function isGadgetPrefsDescriptionValid( $prefsJson ) {
                $prefs = FormatJson::decode( $prefsJson, true );
                
@@ -610,24 +630,89 @@
 
        //Get user's preferences for a specific gadget
        public static function getUserPrefs( $user, $gadget ) {
-               //TODO
-               //for now, we just return defaults
-               $prefsJson = Gadget::getGadgetPrefsDescription( $gadget );
+               //TODO: cache!
+
+               $prefsDescriptionJson = Gadget::getGadgetPrefsDescription( 
$gadget );
                
-               if ( $prefsJson === null || $prefsJson === '' ) {
+               if ( $prefsDescriptionJson === null || $prefsDescriptionJson 
=== '' ) {
                        return null;
                }
                
-               $prefs = FormatJson::decode( $prefsJson, true );
+               $prefsDescription = FormatJson::decode( $prefsDescriptionJson, 
true );
                
-               $userPrefs = array();
                
-               foreach ( $prefs as $pref => $value ) {
-                       $userPrefs[$pref] = $value["default"];
+               $dbr = wfGetDB( DB_SLAVE );
+               
+               $id = $user->getId();
+               $property = "gadget-{$gadget}-config";
+
+               $res = $dbr->selectRow(
+                       'user_properties',
+                       array('up_value'),
+                       array(
+                               "up_user={$id}",
+                               "up_property='{$property}'"
+                       ),
+                       __METHOD__);
+               
+               
+               if ( !$res ) {
+                       return null;
                }
-
+               
+               $userPrefsJson = $res->up_value;
+               
+               $userPrefs = FormatJson::decode( $userPrefsJson, true );
+               
+               //TODO: validate against description, fix mismatches
+               
                return $userPrefs;
        }
+
+       //Set user's preferences for a specific gadget
+       public static function setUserPrefs( $user, $gadget, $preferences ) {
+               //TODO: proper param checking
+               
+               $preferencesJson = FormatJson::encode( $preferences );
+               
+               $dbw = wfGetDB( DB_MASTER );
+               
+               $id = $user->getId();
+               $property = "gadget-{$gadget}-config";
+               
+               $row = array(
+                               'up_user'     => $id,
+                               'up_property' => $property,
+                               'up_value'    => $preferencesJson
+                       );
+                               
+               //Could probably be done with the "ON DUPLICATE KEY UPDATE" 
syntax
+               
+               $res = $dbw->update(
+                       'user_properties',
+                       $row,
+                       array(
+                               "up_user={$id}",
+                               "up_property='{$property}'"
+                       ),
+                       __METHOD__
+               );
+
+
+               $rc = $dbw->affectedRows();
+               if ( $rc == 0 ) {
+                       $dbw->insert(
+                               'user_properties',
+                               $row,
+                               __METHOD__,
+                               array( 'IGNORE' ) //ignore insertions without 
any changes
+                       );
+               }
+
+               //Invalidate cache and update user_touched
+               $user->invalidateCache( true );
+       }
+
 }
 
 /**
@@ -664,26 +749,52 @@
         * @return Array: Names of resources this module depends on
         */
        public function getDependencies() {
+               //return array_merge( $this->dependencies, array( 
'mediawiki.user' ) );
                return $this->dependencies;
        }
+       
+       public function getScript( ResourceLoaderContext $context ) {
+               $moduleName = $this->getName();
+               $gadget = substr( $moduleName, strlen( 'ext.gadget.' ) );
+               
+               
+               $user = RequestContext::getMain()->getUser();
+               
+               $prefs = Gadget::getUserPrefs( $user, $gadget );
+               
+               //Enclose gadget's code in a closure, with "this" bound to the
+               //configuration object (or to "window" for non-configurable 
gadgets)
+               $header = '(function(){';
+               
+               $boundObject = array( 'config' => $prefs );
+               
+               if ( $prefs !== NULL ) {
+                       //Bind configuration object to "this".
+                       //TODO: would be nice add other metadata for the gadget
+                       $footer = '}).' . Xml::encodeJsCall( 'apply', 
+                               array( $boundObject, array() )
+                       ) . ';';
+               } else {
+                       //Bind window to "this"
+                       $footer = '}).apply( window, [] );';
+               }
+               
+               return $header . parent::getScript( $context ) . $footer;
+       }
+       
+       
+       public function getModifiedTime( ResourceLoaderContext $context ) {
+               $touched = RequestContext::getMain()->getUser()->getTouched();
+               
+               return max( parent::getModifiedTime( $context ), $touched );
+       }
 }
 
-
-//Module to tweak Special:Preferences
-class GadgetsSpecialPreferencesTweaksModule extends ResourceLoaderFileModule {
-       public function __construct() {
-               parent::__construct( array(
-                               'scripts'               => array( 
'ext.gadgets.preferences.js' ),
-                               'dependencies'  => array( 'jquery', 
'jquery.ui.dialog', 'mediawiki.htmlform' ),
-                               'localBasePath' => dirname( __FILE__ ) . 
'/modules/',
-                               'remoteExtPath' => 'Gadgets/modules'
-                       )
-               );
-       }
+//Implements ext.gadgets. Required by ext.gadgets.preferences
+class GadgetsGlobalModule extends ResourceLoaderModule {
+       //TODO: should override getModifiedTime()
        
        public function getScript( ResourceLoaderContext $context ) {
-               global $wgUser;
-               
                $configurableGadgets = array();
                $gadgetsList = Gadget::loadStructuredList();
                
@@ -695,14 +806,10 @@
                                }
                        }
                }
-               
-               //TODO: broken in debug mode
-               //create the mw.gadgets object
+
                $script = "mw.gadgets = {}\n";
-               //needed by ext.gadgets.preferences.js
                $script .= "mw.gadgets.configurableGadgets = " . 
Xml::encodeJsVar( $configurableGadgets ) . ";\n";
-               $script .= parent::getScript( $context );
-               
                return $script;
        }
 }
+


Property changes on: branches/salvatoreingala/Gadgets/Gadgets_body.php
___________________________________________________________________
Modified: svn:mergeinfo
   - /branches/Gadgets-work/Gadgets_body.php:73145-76526
/branches/wmf/1.17wmf1/extensions/Gadgets/Gadgets_body.php:81884
   + /branches/Gadgets-work/Gadgets_body.php:73145-76526
/branches/wmf/1.17wmf1/extensions/Gadgets/Gadgets_body.php:81884
/trunk/extensions/Gadgets/Gadgets_body.php:88253-88717

Modified: branches/salvatoreingala/Gadgets/Gadgets_tests.php
===================================================================
--- branches/salvatoreingala/Gadgets/Gadgets_tests.php  2011-05-27 17:59:32 UTC 
(rev 88985)
+++ branches/salvatoreingala/Gadgets/Gadgets_tests.php  2011-05-27 18:13:15 UTC 
(rev 88986)
@@ -45,8 +45,20 @@
        }
 
        function testPreferences() {
-               global $wgUser, $wgOut;
+               global $wgUser;
+
+               // This test makes call to the parser which requires valids 
Outputpage
+               // and Title objects. Set them up there, they will be released 
at the
+               // end of the test.
+               global $wgOut, $wgTitle;
+               $old_wgOut = $wgOut;
+               $old_wgTitle = $wgTitle;
+               $wgTitle = Title::newFromText( 'Parser test for Gadgets 
extension' );
+
+               // Proceed with test setup:
                $prefs = array();
+               $context = new RequestContext();
+               $wgOut = $context->getOutput();
                $wgOut->setTitle( Title::newFromText( 'test' ) );
 
                Gadget::loadStructuredList( '* foo | foo.js
@@ -62,5 +74,9 @@
                $this->assertFalse( isset( 
$options['&lt;gadget-section-remove-section&gt;'] ), 'Must not show empty 
sections' );
                $this->assertTrue( isset( 
$options['&lt;gadget-section-keep-section1&gt;'] ) );
                $this->assertTrue( isset( 
$options['&lt;gadget-section-keep-section2&gt;'] ) );
+
+               // Restore globals
+               $wgOut = $old_wgOut;
+               $wgTitle = $old_wgTitle;
        }
-}
\ No newline at end of file
+}

Modified: branches/salvatoreingala/Gadgets/modules/ext.gadgets.preferences.js
===================================================================
--- branches/salvatoreingala/Gadgets/modules/ext.gadgets.preferences.js 
2011-05-27 17:59:32 UTC (rev 88985)
+++ branches/salvatoreingala/Gadgets/modules/ext.gadgets.preferences.js 
2011-05-27 18:13:15 UTC (rev 88986)
@@ -2,6 +2,45 @@
  * JavaScript tweaks for Special:Preferences
  */
 ( function( $, mw ) {
+       
+       //"Save" button click handler
+       function saveConfig( $dialog, gadget ) {
+               var config = {};
+               
+               //Inputs are all the children of $dialog whose id starts with 
"mw-input-wp"
+               //TODO: fix this, there is no warranty that this doesn't 
conflicts with other existing ids.
+               $dialog.find( '[id ^= "mw-input-wp"]' ).each( function( i, el ) 
{                       
+                       var param = el.id.substring( "mw-input-wp".length );
+                       config[param] = $( el ).val(); //TODO: this only work 
for simpler fields
+               } );
+               
+               var json = $.toJSON( config );
+               
+               var postData = 'action=ajax&rs=GadgetsAjax::setPreferences' +
+                               '&rsargs[]=gadget|' + encodeURIComponent( 
gadget ) +
+                               '&rsargs[]=json|' + encodeURIComponent( json );
+
+               $.ajax( {
+                       url: mw.config.get( 'wgScriptPath' ) + '/index.php',
+                       type: "POST",
+                       data: postData,
+                       dataType: "json",
+                       success: function( response ) {
+                               if ( response === true ) {
+                                       alert( 'Configuration saved 
successfully' );
+                                       $dialog.dialog( 'close' );
+                               } else {
+                                       //TODO
+                                       alert( 'Something wrong happened' );
+                               }
+                       },
+                       error: function( response ) {
+                               //TODO
+                               alert( 'Something wrong happened' );
+                       }
+               } );
+       }
+       
        $( '#mw-htmlform-gadgets input[name="wpgadgets[]"]' ).each( function( 
idx, input ) {
                var     id = input.id,
                        gadget = id.substr( "mw-input-wpgadgets-".length );
@@ -18,13 +57,13 @@
                                .click( function() {
                                        var postData = 
'action=ajax&rs=GadgetsAjax::getUI' +
                                                        '&rsargs[]=gadget|' + 
encodeURIComponent( gadget );
-                                       // Send POST request via AJAX!
+                                       
                                        $.ajax( {
                                                url: mw.config.get( 
'wgScriptPath' ) + '/index.php',
                                                type: "POST",
                                                data: postData,
                                                dataType: "html", // response 
type
-                                               success : function( response ) {
+                                               success: function( response ) {
                                                        //Show dialog
                                                        $( response ).dialog( {
                                                                modal: true,
@@ -32,13 +71,12 @@
                                                                resizable: 
false,
                                                                title: 
'Configuration of ' + gadget, //TODO: use messages
                                                                close: 
function() {
-                                                                       
$(this).dialog('destroy').empty(); //completely destroy on close
+                                                                       $( this 
).dialog( 'destroy' ).empty(); //completely destroy on close
                                                                },
                                                                buttons: {
                                                                        //TODO: 
add "Restore defaults" button
                                                                        "Save": 
function() {
-                                                                               
//TODO
-                                                                               
alert( "I should save changes now, but I don't know how :'(..." );
+                                                                               
saveConfig( $( this ), gadget );
                                                                        },
                                                                        
"Cancel": function() {
                                                                                
$( this ).dialog( "close" );
@@ -49,7 +87,7 @@
                                                error: function( response ) {
                                                        //TODO
                                                        alert( 'Something wrong 
happened' );
-                                               },
+                                               }
                                        } );
 
                                        return false; //prevent event 
propagation
@@ -65,3 +103,4 @@
                }
        } );
 } )( jQuery, mediaWiki );
+


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

Reply via email to