r73519 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r73518‎ | r73519 | r73520 >
Date:12:31, 22 September 2010
Author:catrope
Status:deferred
Tags:
Comment:
ArticleAssessmentPilot: Add support for combining and minifying JS
Modified paths:
  • /trunk/extensions/ArticleAssessmentPilot/ArticleAssessmentPilot.hooks.php (modified) (history)
  • /trunk/extensions/ArticleAssessmentPilot/ArticleAssessmentPilot.php (modified) (history)
  • /trunk/extensions/ArticleAssessmentPilot/Makefile (added) (history)
  • /trunk/extensions/ArticleAssessmentPilot/js/ArticleAssessment.combined.js (added) (history)
  • /trunk/extensions/ArticleAssessmentPilot/js/ArticleAssessment.combined.min.js (added) (history)

Diff [purge]

Index: trunk/extensions/ArticleAssessmentPilot/ArticleAssessmentPilot.php
@@ -11,6 +11,9 @@
1212 // Extension is "disabled" if this field is an empty string (as per default configuration)
1313 $wgArticleAssessmentCategory = '';
1414
 15+// Set to 'combined' or 'raw' if you need to debug this extension's JS
 16+$wgArticleAssessmentResourceMode = 'minified';
 17+
1518 // Path to jQuery UI
1619 $wgArticleAssessmentJUIPath = null; // Defaults to "$wgExtensionAssetsPath/ArticleAssessmentPilot/js/jui.combined.min.js"
1720
Index: trunk/extensions/ArticleAssessmentPilot/ArticleAssessmentPilot.hooks.php
@@ -8,14 +8,30 @@
99 */
1010 class ArticleAssessmentPilotHooks {
1111 private static $styleFiles = array(
12 - array( 'src' => 'css/ArticleAssessment.css', 'version' => 1 ),
 12+ 'raw' => array(
 13+ array( 'src' => 'css/ArticleAssessment.css', 'version' => 1 ),
 14+ ),
 15+ 'combined' => array(
 16+ array( 'src' => 'css/ArticleAssessment.css', 'version' => 1 ),
 17+ ),
 18+ 'minified' => array(
 19+ array( 'src' => 'css/ArticleAssessment.css', 'version' => 1 ),
 20+ ),
1321 );
1422
1523 private static $scriptFiles = array(
16 - array( 'src' => 'js/ArticleAssessment.js', 'version' => 2 ),
17 - array( 'src' => 'js/jquery.cookie.js', 'version' => 1 ),
18 - array( 'src' => 'js/jquery.tipsy.js', 'version' => 1 ),
19 - array( 'src' => 'js/jquery.stars.js', 'version' => 1 ),
 24+ 'raw' => array(
 25+ array( 'src' => 'js/ArticleAssessment.js', 'version' => 2 ),
 26+ array( 'src' => 'js/jquery.cookie.js', 'version' => 1 ),
 27+ array( 'src' => 'js/jquery.tipsy.js', 'version' => 1 ),
 28+ array( 'src' => 'js/jquery.stars.js', 'version' => 1 ),
 29+ ),
 30+ 'combined' => array(
 31+ array( 'src' => 'js/ArticleAssessment.combined.js', 'version' => 1 )
 32+ ),
 33+ 'minified' => array(
 34+ array( 'src' => 'js/ArticleAssessment.combined.min.js', 'version' => 1 )
 35+ ),
2036 );
2137
2238 private static $messages = array();
@@ -78,9 +94,9 @@
7995 return true;
8096 }
8197
82 - global $wgExtensionAssetsPath;
83 -
84 - foreach ( self::$scriptFiles as $script ) {
 98+ global $wgExtensionAssetsPath, $wgArticleAssessmentResourceMode;
 99+ $mode = isset( self::$scriptFiles[$wgArticleAssessmentResourceMode] ) ? $wgArticleAssessmentResourceMode : 'minified';
 100+ foreach ( self::$scriptFiles[$mode] as $script ) {
85101 $out->addScriptFile( $wgExtensionAssetsPath .
86102 "/ArticleAssessmentPilot/{$script['src']}", $script['version']
87103 );
@@ -88,9 +104,9 @@
89105
90106 global $wgArticleAssessmentNeedJUICSS;
91107 if ( $wgArticleAssessmentNeedJUICSS ) {
92 - self::$styleFiles[] = array( 'src' => 'css/jquery-ui-1.7.2.css', 'version' => '1.7.2y' );
 108+ self::$styleFiles[$mode][] = array( 'src' => 'css/jquery-ui-1.7.2.css', 'version' => '1.7.2y' );
93109 }
94 - foreach ( self::$styleFiles as $style ) {
 110+ foreach ( self::$styleFiles[$mode] as $style ) {
95111 $out->addExtensionStyle( $wgExtensionAssetsPath .
96112 "/ArticleAssessmentPilot/{$style['src']}?{$style['version']}"
97113 );
Index: trunk/extensions/ArticleAssessmentPilot/js/ArticleAssessment.combined.js
@@ -0,0 +1,1125 @@
 2+( function( $ ) {
 3+ $.ArticleAssessment = {
 4+ 'config': {
 5+ 'authtoken': '',
 6+ 'userID': '',
 7+ 'pageID': wgArticleId,
 8+ 'revID': wgCurRevisionId
 9+ },
 10+ 'messages': {},
 11+ 'settings': {
 12+ 'endpoint': wgScriptPath + '/api.php?',
 13+ 'fieldMessages' : [
 14+ 'wellsourced',
 15+ 'neutrality',
 16+ 'completeness',
 17+ 'readability'
 18+ ],
 19+ 'fieldHintSuffix': '-tooltip',
 20+ 'fieldPrefix': 'articleassessment-rating-',
 21+ 'fieldHTML': '<div class="field-wrapper"> \
 22+ <label class="rating-field-label"></label> \
 23+ <select class="rating-field"> \
 24+ <option value="1">1</option> \
 25+ <option value="2">2</option> \
 26+ <option value="3">3</option> \
 27+ <option value="4">4</option> \
 28+ <option value="5">5</option> \
 29+ </select> \
 30+ </div>',
 31+ 'structureHTML': '<div class="article-assessment-wrapper"> \
 32+ <form action="rate" method="post" id="article-assessment"> \
 33+ <fieldset id="article-assessment-rate"> \
 34+ <legend></legend> \
 35+ <div class="article-assessment-information"> \
 36+ <span class="article-assessment-rate-instructions"></span> \
 37+ <span class="article-assessment-rate-feedback"></span> \
 38+ </div> \
 39+ <div class="article-assessment-rating-fields"></div> \
 40+ <div class="article-assessment-submit"> \
 41+ <input type="submit" value="Submit" /> \
 42+ </div> \
 43+ </fieldset> \
 44+ <fieldset id="article-assessment-ratings"> \
 45+ <legend></legend> \
 46+ <div class="article-assessment-information"> \
 47+ <span class="article-assessment-show-ratings"></span> \
 48+ <span class="article-assessment-hide-ratings"></span> \
 49+ </div> \
 50+ </fieldset> \
 51+ </form> \
 52+ </div>',
 53+ 'ratingHTML': '<div class="article-assessment-rating"> \
 54+ <span class="article-assessment-rating-field-name"></span> \
 55+ <span class="article-assessment-rating-field-value-wrapper"> \
 56+ <span class="article-assessment-rating-field-value">0%</span> \
 57+ </span> \
 58+ <span class="article-assessment-rating-count"></span> \
 59+ </div>'
 60+ },
 61+
 62+ 'fn' : {
 63+ 'init': function( $$options ) {
 64+ // merge options with the config
 65+ var settings = $.extend( {}, $.ArticleAssessment.settings, $$options );
 66+ var config = $.ArticleAssessment.config;
 67+ // if this is an anon user, get a unique identifier for them
 68+ // load up the stored ratings and update the markup if the cookie exists
 69+ var userToken = $.cookie( 'mwArticleAssessmentUserToken' );
 70+ if ( typeof userToken == 'undefined' || userToken == null ) {
 71+ function randomString( string_length ) {
 72+ var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
 73+ var randomstring = '';
 74+ for ( var i = 0; i < string_length; i++ ) {
 75+ var rnum = Math.floor( Math.random() * chars.length );
 76+ randomstring += chars.substring( rnum, rnum + 1 );
 77+ }
 78+ return randomstring;
 79+ }
 80+ userToken = randomString( 32 );
 81+ $.cookie( 'mwArticleAssessmentUserToken', userToken, { 'expires': 30, 'path': '/' } );
 82+ }
 83+ if ( !wgUserName ) {
 84+ config.userID = userToken;
 85+ }
 86+ // setup our markup using the template variables in settings
 87+ var $structure = $( settings.structureHTML ),
 88+ instructions = $.ArticleAssessment.fn.getMsg( 'articleassessment-pleaserate' ),
 89+ feedback = $.ArticleAssessment.fn.getMsg( 'articleassessment-featurefeedback' ),
 90+ yourfeedback = $.ArticleAssessment.fn.getMsg( 'articleassessment-yourfeedback'),
 91+ articlerating = $.ArticleAssessment.fn.getMsg( 'articleassessment-articlerating' ),
 92+ resultshide = $.ArticleAssessment.fn.getMsg( 'articleassessment-results-hide' ),
 93+ resultsshow = $.ArticleAssessment.fn.getMsg( 'articleassessment-results-show' );
 94+ submitbutton = $.ArticleAssessment.fn.getMsg( 'articleassessment-submit' );
 95+ $structure
 96+ .find( '#article-assessment-rate legend' )
 97+ .text( yourfeedback )
 98+ .end()
 99+ .find( '.article-assessment-rate-instructions' )
 100+ .text( instructions )
 101+ .end()
 102+ .find( '.article-assessment-rate-feedback' )
 103+ .html( feedback )
 104+ .find( '.feedbacklink' )
 105+ .wrap( '<a href="#"></a>' )
 106+ .parent()
 107+ .click( $.ArticleAssessment.fn.showFeedback )
 108+ .end()
 109+ .end()
 110+ .end()
 111+ .find( '#article-assessment-ratings legend' )
 112+ .text( articlerating )
 113+ .end()
 114+ .find( '.article-assessment-show-ratings' )
 115+ .html( resultsshow )
 116+ .find( '.showlink' )
 117+ .wrap( '<a href="#"></a>' )
 118+ .parent()
 119+ .click( $.ArticleAssessment.fn.showRatings )
 120+ .end()
 121+ .end()
 122+ .end()
 123+ .find( '.article-assessment-hide-ratings' )
 124+ .html( resultshide )
 125+ .find ( '.hidelink' )
 126+ .wrap( '<a href="#"></a>' )
 127+ .parent()
 128+ .click( $.ArticleAssessment.fn.hideRatings )
 129+ .end()
 130+ .end()
 131+ .end()
 132+ .find( '.article-assessment-submit input' )
 133+ .attr( "value", submitbutton )
 134+ .end();
 135+ // hide the feedback link if we need to
 136+ if( $.cookie( 'mwArticleAssessmentHideFeedback' ) ) {
 137+ $structure
 138+ .find( '.article-assessment-rate-feedback' )
 139+ .hide();
 140+ }
 141+ for ( var i = 0; i < settings.fieldMessages.length; i++ ) {
 142+ var $field = $( settings.fieldHTML ),
 143+ $rating = $( settings.ratingHTML ),
 144+ label = $.ArticleAssessment.fn.getMsg( settings.fieldPrefix + settings.fieldMessages[i] ),
 145+ field = settings.fieldMessages[i],
 146+ hint = $.ArticleAssessment.fn.getMsg( settings.fieldPrefix + settings.fieldMessages[i] + settings.fieldHintSuffix ),
 147+ count = $.ArticleAssessment.fn.getMsg( 'articleassessment-noratings', [0, 0] );
 148+ // initialize the field html
 149+ $field
 150+ .attr( 'id', 'articleassessment-rate-' + field )
 151+ .find( 'label' )
 152+ .attr( 'for', 'rating_' + field )
 153+ .attr( 'original-title', hint )
 154+ .text( label )
 155+ .end()
 156+ .find( 'select' )
 157+ .attr( 'id', 'rating_' + field )
 158+ .attr( 'name', 'rating[' + field + ']' );
 159+ // initialize the rating html
 160+ $rating
 161+ .attr( 'id', 'articleassessment-rating-' + field )
 162+ .find( '.article-assessment-rating-field-name' )
 163+ .text( label )
 164+ .end()
 165+ .find( '.article-assessment-rating-count' )
 166+ .text( count );
 167+ // append the field and rating html
 168+ $structure
 169+ .find( '.article-assessment-rating-fields' )
 170+ .append( $field )
 171+ .end()
 172+ .find( '#article-assessment-ratings' )
 173+ .append( $rating );
 174+ }
 175+ // store our settings and configuration for later
 176+ $structure.find( '#article-assessment' ).data( 'articleAssessment-context', { 'settings': settings, 'config': config } );
 177+ $( '#catlinks' ).before( $structure );
 178+ // Hide the ratings initially
 179+ $.ArticleAssessment.fn.hideRatings();
 180+
 181+
 182+ // set the height of our smaller fieldset to match the taller
 183+ if ( $( '#article-assessment-rate' ).height() > $( '#article-assessment-ratings' ).height() ) {
 184+ $( '#article-assessment-ratings' ).css( 'minHeight', $( '#article-assessment-rate' ).height() );
 185+ } else {
 186+ $( '#article-assessment-rate' ).css( 'minHeight', $( '#article-assessment-ratings' ).height() );
 187+ }
 188+ // attempt to fetch the ratings
 189+ $.ArticleAssessment.fn.getRatingData();
 190+
 191+ // initialize the star plugin
 192+ $( '.rating-field' ).each( function() {
 193+ $( this )
 194+ .wrapAll( '<div class="rating-field"></div>' )
 195+ .parent()
 196+ .stars( {
 197+ inputType: 'select',
 198+ callback: function( value, link ) {
 199+ // remove any stale or rated classes
 200+ value.$stars.each( function() {
 201+ $( this )
 202+ .removeClass( 'ui-stars-star-stale' )
 203+ .removeClass( 'ui-stars-star-rated' );
 204+ // enable our submit button if it's still disabled
 205+ $( '#article-assessment input:disabled' ).removeAttr( 'disabled' );
 206+ } );
 207+ }
 208+ } );
 209+ });
 210+ // intialize the tooltips
 211+ $( '.field-wrapper label[original-title]' ).each( function() {
 212+ $( this )
 213+ .after( $( '<span class="rating-field-hint" />' )
 214+ .attr( 'original-title', $( this ).attr( 'original-title' ) )
 215+ .tipsy( { gravity : 'se', opacity: '0.9' } ) );
 216+ } );
 217+ // bind submit event to the form
 218+ $( '#article-assessment' )
 219+ .submit( function() { $.ArticleAssessment.fn.submitRating(); return false; } );
 220+ // prevent the submit button for being active until all ratings are filled out
 221+ $( '#article-assessment input[type=submit]' )
 222+ .attr( 'disabled', 'disabled' );
 223+ },
 224+ 'showRatings': function() {
 225+ $( '#article-assessment-ratings' )
 226+ .removeClass( 'article-assessment-ratings-disabled' )
 227+ .find( '.article-assessment-show-ratings' )
 228+ .hide()
 229+ .end()
 230+ .find( '.article-assessment-hide-ratings' )
 231+ .show();
 232+ return false;
 233+ },
 234+ 'hideRatings': function() {
 235+ $( '#article-assessment-ratings' )
 236+ .addClass( 'article-assessment-ratings-disabled' )
 237+ .find( '.article-assessment-hide-ratings' )
 238+ .hide()
 239+ .end()
 240+ .find( '.article-assessment-show-ratings' )
 241+ .show();
 242+ return false;
 243+
 244+ },
 245+
 246+ // Request the ratings data for the current article
 247+ 'getRatingData': function() {
 248+ var config = $( '#article-assessment' ).data( 'articleAssessment-context' ).config;
 249+ var requestData = {
 250+ 'action': 'query',
 251+ 'list': 'articleassessment',
 252+ 'aapageid': config.pageID,
 253+ 'aauserrating': 1,
 254+ 'format': 'json'
 255+ }
 256+ if ( config.userID.length == 32 ) {
 257+ requestData.aaanontoken = config.userID;
 258+ }
 259+
 260+ var request = $.ajax( {
 261+ url: wgScriptPath + '/api.php',
 262+ data: requestData,
 263+ dataType: 'json',
 264+ success: $.ArticleAssessment.fn.afterGetRatingData,
 265+ error: function( XMLHttpRequest, textStatus, errorThrown ) {
 266+ $.ArticleAssessment.fn.flashNotice( $.ArticleAssessment.fn.getMsg( 'articleassessment-error' ),
 267+ { 'class': 'article-assessment-error-msg' } );
 268+ }
 269+ } );
 270+ },
 271+ 'afterGetRatingData' : function( data ) {
 272+ var settings = $( '#article-assessment' ).data( 'articleAssessment-context' ).settings;
 273+ // add the correct data to the markup
 274+ if ( data.query && data.query.articleassessment && data.query.articleassessment.length > 0 ) {
 275+ for ( var r in data.query.articleassessment[0].ratings ) {
 276+ var rating = data.query.articleassessment[0].ratings[r],
 277+ $rating = $( '#' + rating.ratingdesc ),
 278+ count = rating.count,
 279+ total = ( rating.total / count ).toFixed( 1 ),
 280+ label = $.ArticleAssessment.fn.getMsg( 'articleassessment-noratings', [total, count] );
 281+ $rating
 282+ .find( '.article-assessment-rating-field-value' )
 283+ .text( total )
 284+ .end()
 285+ .find( '.article-assessment-rating-count' )
 286+ .text( label );
 287+ if( rating.userrating ) {
 288+ // this user rated. Word. Show them their ratings
 289+ var $rateControl = $( '#' + rating.ratingdesc.replace( 'rating', 'rate' ) + ' .rating-field' );
 290+ $rateControl.stars( 'select', rating.userrating );
 291+ // oh let's show them the overall ratings too
 292+ $.ArticleAssessment.fn.showRatings();
 293+ }
 294+ }
 295+ // if the rating is more than 5 revisions old, mark it as stale
 296+ if ( typeof data.query.articleassessment[0].stale != 'undefined' ) {
 297+ // add the stale star class to each on star
 298+ $( '.ui-stars-star-on' )
 299+ .addClass( 'ui-stars-star-stale' );
 300+ // add the stale message
 301+ var msg = $.ArticleAssessment.fn.getMsg( 'articleassessment-stalemessage-norevisioncount' );
 302+ $.ArticleAssessment.fn.flashNotice( msg, { 'class': 'article-assessment-stale-msg' } );
 303+ } else {
 304+ // if it's not a stale rating, we want to make the stars blue
 305+ $( '.ui-stars-star-on' ).addClass( 'ui-stars-star-rated' );
 306+ }
 307+ }
 308+ // initialize the ratings
 309+ $( '.article-assessment-rating-field-value' ).each( function() {
 310+ $( this )
 311+ .css( {
 312+ 'width': 120 - ( 120 * ( parseFloat( $( this ).text() ) / 5 ) ) + 'px'
 313+ } )
 314+ } );
 315+ },
 316+ 'submitRating': function() {
 317+ var config = $( '#article-assessment' ).data( 'articleAssessment-context' ).config;
 318+ // clear out the stale message
 319+ $.ArticleAssessment.fn.flashNotice( );
 320+
 321+ // lock the star inputs & submit
 322+ $( '.rating-field' ).stars( 'disable' );
 323+ $( '#article-assessment input' ).attr( 'disabled', 'disabled' );
 324+ // get our results for submitting
 325+ var results = {};
 326+ $( '.rating-field input' ).each( function() {
 327+ // expects the hidden inputs to have names like 'rating[field-name]' which we use to
 328+ // be transparent about what values we're sending to the server
 329+ var fieldName = $( this ).attr( 'name' ).match( /\[([a-zA-Z0-9\-]*)\]/ )[1];
 330+ results[ fieldName ] = $( this ).val();
 331+ } );
 332+ var request = $.ajax( {
 333+ url: wgScriptPath + '/api.php',
 334+ type: 'POST',
 335+ data: {
 336+ 'action': 'articleassessment',
 337+ 'revid': config.revID,
 338+ 'pageid': config.pageID,
 339+ 'r1' : results['wellsourced'],
 340+ 'r2' : results['neutrality'],
 341+ 'r3' : results['completeness'],
 342+ 'r4' : results['readability'],
 343+ 'anontoken': config.userID,
 344+ 'format': 'json'
 345+ },
 346+ dataType: 'json',
 347+ success: $.ArticleAssessment.fn.afterSubmitRating,
 348+ error: function( XMLHttpRequest, textStatus, errorThrown ) {
 349+ $.ArticleAssessment.fn.flashNotice( $.ArticleAssessment.fn.getMsg( 'articleassessment-error' ),
 350+ { 'class': 'article-assessment-error-msg' } );
 351+ }
 352+ } );
 353+ },
 354+ 'afterSubmitRating': function ( data ) {
 355+ // update the ratings
 356+ $.ArticleAssessment.fn.getRatingData();
 357+ // set the stars to rated status
 358+ $( '.ui-stars-star-on' ).addClass( 'ui-stars-star-rated' );
 359+ // unlock the stars & submit
 360+ $( '.rating-field' ).stars( 'enable' );
 361+ $( '#article-assessment input:disabled' ).removeAttr( 'disabled' );
 362+ // update the results
 363+
 364+ // show the results
 365+ $.ArticleAssessment.fn.showRatings();
 366+ // say thank you
 367+ $.ArticleAssessment.fn.flashNotice( $.ArticleAssessment.fn.getMsg( 'articleassessment-thanks' ),
 368+ { 'class': 'article-assessment-success-msg' } );
 369+ },
 370+ // places a message on the interface
 371+ 'flashNotice': function( text, options ) {
 372+ if ( arguments.length == 0 ) {
 373+ // clear existing messages, but don't add a new one
 374+ $( '#article-assessment .article-assessment-flash' ).remove();
 375+ } else {
 376+ // clear and add a new message
 377+ $( '#article-assessment .article-assessment-flash' ).remove();
 378+ var className = options['class'];
 379+ // create our new message
 380+ $msg = $( '<div />' )
 381+ .addClass( 'article-assessment-flash' )
 382+ .html( text );
 383+ // if the class option was passed, add it
 384+ if( options['class'] ) {
 385+ $msg.addClass( options['class'] );
 386+ }
 387+ // place our new message on the page
 388+ $( '#article-assessment .article-assessment-submit' )
 389+ .append( $msg );
 390+ }
 391+ },
 392+ 'showFeedback': function() {
 393+ $.ArticleAssessment.fn.withJUI( function() {
 394+ var $dialogDiv = $( '#article-assessment-dialog' );
 395+ if ( $dialogDiv.size() == 0 ) {
 396+ $dialogDiv = $( '<div id="article-assessment-dialog" class="loading" />' )
 397+ .dialog( {
 398+ width: 600,
 399+ height: 400,
 400+ bgiframe: true,
 401+ autoOpen: true,
 402+ modal: true,
 403+ title: $.ArticleAssessment.fn.getMsg( 'articleassessment-survey-title' ),
 404+ close: function() {
 405+ $( this )
 406+ .dialog( 'option', 'height', 400 )
 407+ .find( '.article-assessment-success-msg, .article-assessment-error-msg' )
 408+ .remove()
 409+ .end()
 410+ .find( 'form' )
 411+ .show();
 412+ }
 413+ } );
 414+ $dialogDiv.load(
 415+ wgScript + '?title=Special:SimpleSurvey&survey=articlerating&raw=1',
 416+ function() {
 417+ $( this ).find( 'form' ).bind( 'submit', $.ArticleAssessment.fn.submitFeedback );
 418+ $( this ).removeClass( 'loading' );
 419+ }
 420+ );
 421+ }
 422+ $dialogDiv.dialog( 'open' );
 423+ } );
 424+ return false;
 425+ },
 426+ 'submitFeedback': function() {
 427+ var $dialogDiv = $( '#article-assessment-dialog' );
 428+ $dialogDiv
 429+ .find( 'form' )
 430+ .hide()
 431+ .end()
 432+ .addClass( 'loading' );
 433+ // Submit straight to the special page. Yes, this is a dirty dirty hack
 434+ // Build request from form data
 435+ var formData = {};
 436+ $dialogDiv.find( 'input' ).each( function() {
 437+ var name = $( this ).attr( 'name' );
 438+ if ( name !== '' ) {
 439+ if ( name.substr( -2 ) == '[]' ) {
 440+ var trimmedName = name.substr( 0, name.length - 2 );
 441+ if ( typeof formData[trimmedName] == 'undefined' ) {
 442+ formData[trimmedName] = [];
 443+ }
 444+ formData[trimmedName].push( $( this ).val() );
 445+ } else {
 446+ formData[name] = $( this ).val();
 447+ }
 448+ }
 449+ } );
 450+ formData.title = 'Special:SimpleSurvey';
 451+
 452+ $.ajax( {
 453+ url: wgScript,
 454+ type: 'POST',
 455+ data: formData,
 456+ dataType: 'html',
 457+ success: function( data ) {
 458+ // This is an evil screenscraping method to determine whether
 459+ // the submission was successful
 460+ var success = $( data ).find( '.simplesurvey-success' ).size() > 0;
 461+ // TODO: Style success-msg, error-msg
 462+ var $msgDiv = $( '<div />' )
 463+ .addClass( success ? 'article-assessment-success-msg' : 'article-assessment-error-msg' )
 464+ .text( $.ArticleAssessment.fn.getMsg( success? 'articleassessment-survey-thanks' : 'articleassessment-error' ) )
 465+ .appendTo( $dialogDiv );
 466+ $dialogDiv
 467+ .dialog( 'option', 'height', $msgDiv.height() + 100 )
 468+ .removeClass( 'loading' );
 469+ if ( success ) {
 470+ // Hide the dialog link
 471+ $( '#article-assessment .article-assessment-rate-feedback' ).hide();
 472+ // set a cookie to keep the dialog link hidden
 473+ $.cookie( 'mwArticleAssessmentHideFeedback', true, { 'expires': 30, 'path': '/' } );
 474+
 475+ }
 476+ },
 477+ error: function( XMLHttpRequest, textStatus, errorThrown ) {
 478+ // TODO: Duplicates code, factor out, maybe
 479+ var $msgDiv = $( '<div />' )
 480+ .addClass( 'article-assessment-error-msg' )
 481+ .text( $.ArticleAssessment.fn.getMsg( 'articleassessment-error' ) )
 482+ .appendTo( $dialogDiv );
 483+ $dialogDiv
 484+ .dialog( 'option', 'height', $msgDiv.height() + 100 )
 485+ .removeClass( 'loading' );
 486+ }
 487+ } );
 488+ return false;
 489+ },
 490+ 'addMessages': function( messages ) {
 491+ for ( var key in messages ) {
 492+ $.ArticleAssessment.messages[key] = messages[key];
 493+ }
 494+ },
 495+ /**
 496+ * Get a message
 497+ * FIXME: Parameter expansion is broken in all sorts of edge cases
 498+ */
 499+ 'getMsg': function( key, args ) {
 500+ if ( !( key in $.ArticleAssessment.messages ) ) {
 501+ return '[' + key + ']';
 502+ }
 503+ var msg = $.ArticleAssessment.messages[key];
 504+ if ( typeof args == 'object' || typeof args == 'array' ) {
 505+ for ( var i = 0; i < args.length; i++ ) {
 506+ msg = msg.replace( new RegExp( '\\$' + ( parseInt( i ) + 1 ), 'g' ), args[i] );
 507+ }
 508+ } else if ( typeof args == 'string' || typeof args == 'number' ) {
 509+ msg = msg.replace( /\$1/g, args );
 510+ }
 511+ return msg;
 512+ },
 513+ 'withJUI': function( callback ) {
 514+ if ( typeof $.ui == 'undefined' ) {
 515+ $.getScript( wgArticleAssessmentJUIPath, callback );
 516+ } else {
 517+ callback();
 518+ }
 519+ }
 520+ }
 521+ };
 522+ $( document ).ready( function () {
 523+ $.ArticleAssessment.fn.init( );
 524+ } ); //document ready
 525+} )( jQuery );
 526+/**
 527+ * Cookie plugin
 528+ *
 529+ * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 530+ * Dual licensed under the MIT and GPL licenses:
 531+ * http://www.opensource.org/licenses/mit-license.php
 532+ * http://www.gnu.org/licenses/gpl.html
 533+ *
 534+ */
 535+
 536+/**
 537+ * Create a cookie with the given name and value and other optional parameters.
 538+ *
 539+ * @example $.cookie('the_cookie', 'the_value');
 540+ * @desc Set the value of a cookie.
 541+ * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 542+ * @desc Create a cookie with all available options.
 543+ * @example $.cookie('the_cookie', 'the_value');
 544+ * @desc Create a session cookie.
 545+ * @example $.cookie('the_cookie', null);
 546+ * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 547+ * used when the cookie was set.
 548+ *
 549+ * @param String name The name of the cookie.
 550+ * @param String value The value of the cookie.
 551+ * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 552+ * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 553+ * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 554+ * If set to null or omitted, the cookie will be a session cookie and will not be retained
 555+ * when the the browser exits.
 556+ * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 557+ * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 558+ * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 559+ * require a secure protocol (like HTTPS).
 560+ * @type undefined
 561+ *
 562+ * @name $.cookie
 563+ * @cat Plugins/Cookie
 564+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
 565+ */
 566+
 567+/**
 568+ * Get the value of a cookie with the given name.
 569+ *
 570+ * @example $.cookie('the_cookie');
 571+ * @desc Get the value of a cookie.
 572+ *
 573+ * @param String name The name of the cookie.
 574+ * @return The value of the cookie.
 575+ * @type String
 576+ *
 577+ * @name $.cookie
 578+ * @cat Plugins/Cookie
 579+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
 580+ */
 581+jQuery.cookie = function(name, value, options) {
 582+ if (typeof value != 'undefined') { // name and value given, set cookie
 583+ options = options || {};
 584+ if (value === null) {
 585+ value = '';
 586+ options.expires = -1;
 587+ }
 588+ var expires = '';
 589+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
 590+ var date;
 591+ if (typeof options.expires == 'number') {
 592+ date = new Date();
 593+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
 594+ } else {
 595+ date = options.expires;
 596+ }
 597+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
 598+ }
 599+ // CAUTION: Needed to parenthesize options.path and options.domain
 600+ // in the following expressions, otherwise they evaluate to undefined
 601+ // in the packed version for some reason...
 602+ var path = options.path ? '; path=' + (options.path) : '';
 603+ var domain = options.domain ? '; domain=' + (options.domain) : '';
 604+ var secure = options.secure ? '; secure' : '';
 605+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
 606+ } else { // only name given, get cookie
 607+ var cookieValue = null;
 608+ if (document.cookie && document.cookie != '') {
 609+ var cookies = document.cookie.split(';');
 610+ for (var i = 0; i < cookies.length; i++) {
 611+ var cookie = jQuery.trim(cookies[i]);
 612+ // Does this cookie string begin with the name we want?
 613+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
 614+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
 615+ break;
 616+ }
 617+ }
 618+ }
 619+ return cookieValue;
 620+ }
 621+};
 622+
 623+/*!
 624+ * jQuery Stars v1
 625+ * adapted by Adam Miller (acm6603@gmail.com)
 626+ *
 627+ * Adapted from jQuery UI Stars v3.0.1
 628+ * Marek "Orkan" Zajac (orkans@gmail.com)
 629+ * http://plugins.jquery.com/project/Star_Rating_widget
 630+ *
 631+ */
 632+(function($) {
 633+$.stars = {
 634+ defaults : {
 635+ inputType: 'radio', // [radio|select]
 636+ split: 0, // decrease number of stars by splitting each star into pieces [2|3|4|...]
 637+ disabled: false, // set to [true] to make the stars initially disabled
 638+ cancelTitle: 'Cancel Rating',
 639+ cancelValue: 0, // default value of Cancel btn.
 640+ cancelShow: true,
 641+ disableValue: true, // set to [false] to not disable the hidden input when Cancel btn is clicked, so the value will present in POST data.
 642+ oneVoteOnly: false,
 643+ showTitles: false,
 644+ captionEl: null, // jQuery object - target for text captions
 645+ callback: null, // function(ui, type, value, event)
 646+ /*
 647+ * CSS classes
 648+ */
 649+ starWidth: 16, // width of the star image
 650+ cancelClass: 'ui-stars-cancel',
 651+ starClass: 'ui-stars-star',
 652+ starOnClass: 'ui-stars-star-on',
 653+ starHoverClass: 'ui-stars-star-hover',
 654+ starDisabledClass: 'ui-stars-star-disabled',
 655+ cancelHoverClass: 'ui-stars-cancel-hover',
 656+ cancelDisabledClass: 'ui-stars-cancel-disabled'
 657+ },
 658+ create: function( ) {
 659+ var self = this, o = this.options, starId = 0;
 660+ this.element.data('former.stars', this.element.html());
 661+ o.isSelect = o.inputType == 'select';
 662+ this.$form = $(this.element).closest('form');
 663+ this.$selec = o.isSelect ? $('select', this.element) : null;
 664+ this.$rboxs = o.isSelect ? $('option', this.$selec) : $(':radio', this.element);
 665+ /*
 666+ * Map all inputs from $rboxs array to Stars elements
 667+ */
 668+ this.$stars = this.$rboxs.map(function(i)
 669+ {
 670+ var el = {
 671+ value: this.value,
 672+ title: (o.isSelect ? this.text : this.title) || this.value,
 673+ isDefault: (o.isSelect && this.defaultSelected) || this.defaultChecked
 674+ };
 675+ if(i==0) {
 676+ o.split = typeof o.split != 'number' ? 0 : o.split;
 677+ o.val2id = [];
 678+ o.id2val = [];
 679+ o.id2title = [];
 680+ o.name = o.isSelect ? self.$selec.get(0).name : this.name;
 681+ o.disabled = o.disabled || (o.isSelect ? $(self.$selec).attr('disabled') : $(this).attr('disabled'));
 682+ }
 683+ /*
 684+ * Consider it as a Cancel button?
 685+ */
 686+ if(el.value == o.cancelValue) {
 687+ o.cancelTitle = el.title;
 688+ return null;
 689+ }
 690+ o.val2id[el.value] = starId;
 691+ o.id2val[starId] = el.value;
 692+ o.id2title[starId] = el.title;
 693+ if(el.isDefault) {
 694+ o.checked = starId;
 695+ o.value = o.defaultValue = el.value;
 696+ o.title = el.title;
 697+ }
 698+ var $s = $('<div/>').addClass(o.starClass);
 699+ var $a = $('<a/>').attr('title', o.showTitles ? el.title : '').text(el.value);
 700+ /*
 701+ * Prepare division settings
 702+ */
 703+ if(o.split) {
 704+ var oddeven = (starId % o.split);
 705+ var stwidth = Math.floor(o.starWidth / o.split);
 706+ $s.width(stwidth);
 707+ $a.css('margin-left', '-' + (oddeven * stwidth) + 'px');
 708+ }
 709+ starId++;
 710+ return $s.append($a).get(0);
 711+ });
 712+ /*
 713+ * How many Stars?
 714+ */
 715+ o.items = starId;
 716+ /*
 717+ * Remove old content
 718+ */
 719+ o.isSelect ? this.$selec.remove() : this.$rboxs.remove();
 720+ /*
 721+ * Append Stars interface
 722+ */
 723+ this.$cancel = $('<div/>').addClass(o.cancelClass).append( $('<a/>').attr('title', o.showTitles ? o.cancelTitle : '').text(o.cancelValue) );
 724+ o.cancelShow &= !o.disabled && !o.oneVoteOnly;
 725+ o.cancelShow && this.element.append(this.$cancel);
 726+ this.element.append(this.$stars);
 727+ /*
 728+ * Initial selection
 729+ */
 730+ if(o.checked === undefined) {
 731+ o.checked = -1;
 732+ o.value = o.defaultValue = o.cancelValue;
 733+ o.title = '';
 734+ }
 735+ /*
 736+ * The only FORM element, that has been linked to the stars control. The value field is updated on each Star click event
 737+ */
 738+ this.$value = $("<input type='hidden' name='"+o.name+"' value='"+o.value+"' />");
 739+ this.element.append(this.$value);
 740+ /*
 741+ * Attach stars event handler
 742+ */
 743+ this.$stars.bind('click.stars', function(e) {
 744+ if(!o.forceSelect && o.disabled) return false;
 745+ var i = self.$stars.index(this);
 746+ o.checked = i;
 747+ o.value = o.id2val[i];
 748+ o.title = o.id2title[i];
 749+ self.$value.attr({disabled: o.disabled ? 'disabled' : '', value: o.value});
 750+ fillTo(i, false);
 751+ self.disableCancel();
 752+ !o.forceSelect && self.callback(e, 'star');
 753+ })
 754+ .bind('mouseover.stars', function() {
 755+ if(o.disabled) return false;
 756+ var i = self.$stars.index(this);
 757+ fillTo(i, true);
 758+ })
 759+ .bind('mouseout.stars', function() {
 760+ if(o.disabled) return false;
 761+ fillTo(self.options.checked, false);
 762+ });
 763+ /*
 764+ * Attach cancel event handler
 765+ */
 766+ this.$cancel.bind('click.stars', function(e) {
 767+ if(!o.forceSelect && (o.disabled || o.value == o.cancelValue)) return false;
 768+ o.checked = -1;
 769+ o.value = o.cancelValue;
 770+ o.title = '';
 771+ self.$value.val(o.value);
 772+ o.disableValue && self.$value.attr({disabled: 'disabled'});
 773+ fillNone();
 774+ self.disableCancel();
 775+ !o.forceSelect && self.callback(e, 'cancel');
 776+ })
 777+ .bind('mouseover.stars', function() {
 778+ if(self.disableCancel()) return false;
 779+ self.$cancel.addClass(o.cancelHoverClass);
 780+ fillNone();
 781+ self.showCap(o.cancelTitle);
 782+ })
 783+ .bind('mouseout.stars', function() {
 784+ if(self.disableCancel()) return false;
 785+ self.$cancel.removeClass(o.cancelHoverClass);
 786+ self.$stars.triggerHandler('mouseout.stars');
 787+ });
 788+ /*
 789+ * Attach onReset event handler to the parent FORM
 790+ */
 791+ this.$form.bind('reset.stars', function(){
 792+ !o.disabled && self.select(o.defaultValue);
 793+ });
 794+ /*
 795+ * Clean up to avoid memory leaks in certain versions of IE 6
 796+ */
 797+ $(window).unload(function(){
 798+ self.$cancel.unbind('.stars');
 799+ self.$stars.unbind('.stars');
 800+ self.$form.unbind('.stars');
 801+ self.$selec = self.$rboxs = self.$stars = self.$value = self.$cancel = self.$form = null;
 802+ });
 803+ /*
 804+ * Star selection helpers
 805+ */
 806+ function fillTo(index, hover) {
 807+ if(index != -1) {
 808+ var addClass = hover ? o.starHoverClass : o.starOnClass;
 809+ var remClass = hover ? o.starOnClass : o.starHoverClass;
 810+ self.$stars.eq(index).prevAll('.' + o.starClass).andSelf().removeClass(remClass).addClass(addClass);
 811+ self.$stars.eq(index).nextAll('.' + o.starClass).removeClass(o.starHoverClass + ' ' + o.starOnClass);
 812+ self.showCap(o.id2title[index]);
 813+ }
 814+ else fillNone();
 815+ };
 816+ function fillNone() {
 817+ self.$stars.removeClass(o.starOnClass + ' ' + o.starHoverClass);
 818+ self.showCap('');
 819+ };
 820+ /*
 821+ * Finally, set up the Stars
 822+ */
 823+ this.select( o.value );
 824+ o.disabled && this.disable();
 825+ },
 826+ /*
 827+ * Private functions
 828+ */
 829+ disableCancel: function() {
 830+ var o = this.options, disabled = o.disabled || o.oneVoteOnly || (o.value == o.cancelValue);
 831+ if(disabled) this.$cancel.removeClass(o.cancelHoverClass).addClass(o.cancelDisabledClass);
 832+ else this.$cancel.removeClass(o.cancelDisabledClass);
 833+ this.$cancel.css('opacity', disabled ? 0.5 : 1);
 834+ return disabled;
 835+ },
 836+ disableAll: function() {
 837+ var o = this.options;
 838+ this.disableCancel();
 839+ if(o.disabled) this.$stars.filter('div').addClass(o.starDisabledClass);
 840+ else this.$stars.filter('div').removeClass(o.starDisabledClass);
 841+ },
 842+ showCap: function(s) {
 843+ var o = this.options;
 844+ if(o.captionEl) o.captionEl.text(s);
 845+ },
 846+ /*
 847+ * Public functions
 848+ */
 849+ value: function() {
 850+ return this.options.value;
 851+ },
 852+ select: function( val ) {
 853+ var o = this.options, e = (val == o.cancelValue) ? this.$cancel : this.$stars.eq(o.val2id[val]);
 854+ o.forceSelect = true;
 855+ e.triggerHandler('click.stars');
 856+ o.forceSelect = false;
 857+ },
 858+ selectID: function(id) {
 859+ var o = this.options, e = (id == -1) ? this.$cancel : this.$stars.eq(id);
 860+ o.forceSelect = true;
 861+ e.triggerHandler('click.stars');
 862+ o.forceSelect = false;
 863+ },
 864+ enable: function() {
 865+ this.options.disabled = false;
 866+ this.disableAll();
 867+ },
 868+ disable: function() {
 869+ this.options.disabled = true;
 870+ this.disableAll();
 871+ },
 872+ destroy: function() {
 873+ this.$form.unbind('.stars');
 874+ this.$cancel.unbind('.stars').remove();
 875+ this.$stars.unbind('.stars').remove();
 876+ this.$value.remove();
 877+ this.element.unbind('.stars').html(this.element.data('former.stars')).removeData('stars');
 878+ return this;
 879+ },
 880+ callback: function(e, type) {
 881+ var o = this.options;
 882+ o.callback && o.callback(this, type, o.value, e);
 883+ o.oneVoteOnly && !o.disabled && this.disable();
 884+ }
 885+}
 886+$.fn.stars = function ( ) {
 887+ // convert the arguments to an array
 888+ var args = Array.prototype.slice.call(arguments);
 889+ // default value to return -- overwritten by api calls
 890+ var out = $( this );
 891+ $( this ).each( function() {
 892+ // get the context if it's already been initialized
 893+ var context = $( this ).data( 'stars-context' );
 894+ if ( typeof context == 'undefined' || context == null ) {
 895+ // setup the context if it hasn't been yet
 896+ context = $.extend( {}, {
 897+ element: $( this ),
 898+ options: $.stars.defaults
 899+ }, $.stars );
 900+ }
 901+ // Handle various calling styles
 902+ if ( args.length > 0 ) {
 903+ if ( typeof args[0] == 'object' ) {
 904+ // merge the passed options into defaults
 905+ context.options = $.extend( {}, context.options, args[0] );
 906+ // initialize
 907+ $.stars.create.call( context );
 908+ } else if ( typeof args[0] == 'string' ) {
 909+ // API call
 910+ var funcName = args[0];
 911+ // call the function, and if it returns something, store the output in our return var
 912+ out = $.stars[funcName].call( context, args.slice(1) ) || out;
 913+ }
 914+ } else {
 915+ // initialize with the defaults
 916+ $.stars.create.call( context );
 917+ }
 918+ // save our context, bay-bee
 919+ $( this ).data( 'stars-context', context );
 920+ } );
 921+
 922+ return out;
 923+};
 924+} )( jQuery );
 925+// tipsy, facebook style tooltips for jquery
 926+// version 1.0.0a
 927+// (c) 2008-2010 jason frame [jason@onehackoranother.com]
 928+// released under the MIT license
 929+
 930+(function($) {
 931+
 932+ function Tipsy(element, options) {
 933+ this.$element = $(element);
 934+ this.options = options;
 935+ this.enabled = true;
 936+ this.fixTitle();
 937+ }
 938+
 939+ Tipsy.prototype = {
 940+ show: function() {
 941+ var title = this.getTitle();
 942+ if (title && this.enabled) {
 943+ var $tip = this.tip();
 944+
 945+ $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
 946+ $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
 947+ $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
 948+
 949+ var pos = $.extend({}, this.$element.offset(), {
 950+ width: this.$element[0].offsetWidth,
 951+ height: this.$element[0].offsetHeight
 952+ });
 953+
 954+ var actualWidth = $tip[0].offsetWidth, actualHeight = $tip[0].offsetHeight;
 955+ var gravity = (typeof this.options.gravity == 'function')
 956+ ? this.options.gravity.call(this.$element[0])
 957+ : this.options.gravity;
 958+
 959+ var tp;
 960+ switch (gravity.charAt(0)) {
 961+ case 'n':
 962+ tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
 963+ break;
 964+ case 's':
 965+ tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
 966+ break;
 967+ case 'e':
 968+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
 969+ break;
 970+ case 'w':
 971+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
 972+ break;
 973+ }
 974+
 975+ if (gravity.length == 2) {
 976+ if (gravity.charAt(1) == 'w') {
 977+ tp.left = pos.left + pos.width / 2 - 15;
 978+ } else {
 979+ tp.left = pos.left + pos.width / 2 - actualWidth + 15;
 980+ }
 981+ }
 982+
 983+ $tip.css(tp).addClass('tipsy-' + gravity);
 984+
 985+ if (this.options.fade) {
 986+ $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
 987+ } else {
 988+ $tip.css({visibility: 'visible', opacity: this.options.opacity});
 989+ }
 990+ }
 991+ },
 992+
 993+ hide: function() {
 994+ if (this.options.fade) {
 995+ this.tip().stop().fadeOut(function() { $(this).remove(); });
 996+ } else {
 997+ this.tip().remove();
 998+ }
 999+ },
 1000+
 1001+ fixTitle: function() {
 1002+ var $e = this.$element;
 1003+ if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
 1004+ $e.attr('original-title', $e.attr('title') || '').removeAttr('title');
 1005+ }
 1006+ },
 1007+
 1008+ getTitle: function() {
 1009+ var title, $e = this.$element, o = this.options;
 1010+ this.fixTitle();
 1011+ var title, o = this.options;
 1012+ if (typeof o.title == 'string') {
 1013+ title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
 1014+ } else if (typeof o.title == 'function') {
 1015+ title = o.title.call($e[0]);
 1016+ }
 1017+ title = ('' + title).replace(/(^\s*|\s*$)/, "");
 1018+ return title || o.fallback;
 1019+ },
 1020+
 1021+ tip: function() {
 1022+ if (!this.$tip) {
 1023+ this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
 1024+ }
 1025+ return this.$tip;
 1026+ },
 1027+
 1028+ validate: function() {
 1029+ if (!this.$element[0].parentNode) {
 1030+ this.hide();
 1031+ this.$element = null;
 1032+ this.options = null;
 1033+ }
 1034+ },
 1035+
 1036+ enable: function() { this.enabled = true; },
 1037+ disable: function() { this.enabled = false; },
 1038+ toggleEnabled: function() { this.enabled = !this.enabled; }
 1039+ };
 1040+
 1041+ $.fn.tipsy = function(options) {
 1042+
 1043+ if (options === true) {
 1044+ return this.data('tipsy');
 1045+ } else if (typeof options == 'string') {
 1046+ var tipsy = this.data('tipsy');
 1047+ if (tipsy) tipsy[options]();
 1048+ return this;
 1049+ }
 1050+
 1051+ options = $.extend({}, $.fn.tipsy.defaults, options);
 1052+
 1053+ function get(ele) {
 1054+ var tipsy = $.data(ele, 'tipsy');
 1055+ if (!tipsy) {
 1056+ tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
 1057+ $.data(ele, 'tipsy', tipsy);
 1058+ }
 1059+ return tipsy;
 1060+ }
 1061+
 1062+ function enter() {
 1063+ var tipsy = get(this);
 1064+ tipsy.hoverState = 'in';
 1065+ if (options.delayIn == 0) {
 1066+ tipsy.show();
 1067+ } else {
 1068+ tipsy.fixTitle();
 1069+ setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
 1070+ }
 1071+ };
 1072+
 1073+ function leave() {
 1074+ var tipsy = get(this);
 1075+ tipsy.hoverState = 'out';
 1076+ if (options.delayOut == 0) {
 1077+ tipsy.hide();
 1078+ } else {
 1079+ setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
 1080+ }
 1081+ };
 1082+
 1083+ if (!options.live) this.each(function() { get(this); });
 1084+
 1085+ if (options.trigger != 'manual') {
 1086+ var binder = options.live ? 'live' : 'bind',
 1087+ eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
 1088+ eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
 1089+ this[binder](eventIn, enter)[binder](eventOut, leave);
 1090+ }
 1091+
 1092+ return this;
 1093+
 1094+ };
 1095+
 1096+ $.fn.tipsy.defaults = {
 1097+ delayIn: 0,
 1098+ delayOut: 0,
 1099+ fade: false,
 1100+ fallback: '',
 1101+ gravity: 'n',
 1102+ html: false,
 1103+ live: false,
 1104+ offset: 0,
 1105+ opacity: 0.8,
 1106+ title: 'title',
 1107+ trigger: 'hover'
 1108+ };
 1109+
 1110+ // Overwrite this method to provide options on a per-element basis.
 1111+ // For example, you could store the gravity in a 'tipsy-gravity' attribute:
 1112+ // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
 1113+ // (remember - do not modify 'options' in place!)
 1114+ $.fn.tipsy.elementOptions = function(ele, options) {
 1115+ return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
 1116+ };
 1117+
 1118+ $.fn.tipsy.autoNS = function() {
 1119+ return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
 1120+ };
 1121+
 1122+ $.fn.tipsy.autoWE = function() {
 1123+ return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
 1124+ };
 1125+
 1126+})(jQuery);
Index: trunk/extensions/ArticleAssessmentPilot/js/ArticleAssessment.combined.min.js
@@ -0,0 +1,77 @@
 2+
 3+(function($){$.ArticleAssessment={'config':{'authtoken':'','userID':'','pageID':wgArticleId,'revID':wgCurRevisionId},'messages':{},'settings':{'endpoint':wgScriptPath+'/api.php?','fieldMessages':['wellsourced','neutrality','completeness','readability'],'fieldHintSuffix':'-tooltip','fieldPrefix':'articleassessment-rating-','fieldHTML':'<div class="field-wrapper"> \
 4+ <label class="rating-field-label"></label> \
 5+ <select class="rating-field"> \
 6+ <option value="1">1</option> \
 7+ <option value="2">2</option> \
 8+ <option value="3">3</option> \
 9+ <option value="4">4</option> \
 10+ <option value="5">5</option> \
 11+ </select> \
 12+ </div>','structureHTML':'<div class="article-assessment-wrapper"> \
 13+ <form action="rate" method="post" id="article-assessment"> \
 14+ <fieldset id="article-assessment-rate"> \
 15+ <legend></legend> \
 16+ <div class="article-assessment-information"> \
 17+ <span class="article-assessment-rate-instructions"></span> \
 18+ <span class="article-assessment-rate-feedback"></span> \
 19+ </div> \
 20+ <div class="article-assessment-rating-fields"></div> \
 21+ <div class="article-assessment-submit"> \
 22+ <input type="submit" value="Submit" /> \
 23+ </div> \
 24+ </fieldset> \
 25+ <fieldset id="article-assessment-ratings"> \
 26+ <legend></legend> \
 27+ <div class="article-assessment-information"> \
 28+ <span class="article-assessment-show-ratings"></span> \
 29+ <span class="article-assessment-hide-ratings"></span> \
 30+ </div> \
 31+ </fieldset> \
 32+ </form> \
 33+ </div>','ratingHTML':'<div class="article-assessment-rating"> \
 34+ <span class="article-assessment-rating-field-name"></span> \
 35+ <span class="article-assessment-rating-field-value-wrapper"> \
 36+ <span class="article-assessment-rating-field-value">0%</span> \
 37+ </span> \
 38+ <span class="article-assessment-rating-count"></span> \
 39+ </div>'},'fn':{'init':function($$options){var settings=$.extend({},$.ArticleAssessment.settings,$$options);var config=$.ArticleAssessment.config;var userToken=$.cookie('mwArticleAssessmentUserToken');if(typeof userToken=='undefined'||userToken==null){function randomString(string_length){var chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";var randomstring='';for(var i=0;i<string_length;i++){var rnum=Math.floor(Math.random()*chars.length);randomstring+=chars.substring(rnum,rnum+1);}
 40+return randomstring;}
 41+userToken=randomString(32);$.cookie('mwArticleAssessmentUserToken',userToken,{'expires':30,'path':'/'});}
 42+if(!wgUserName){config.userID=userToken;}
 43+var $structure=$(settings.structureHTML),instructions=$.ArticleAssessment.fn.getMsg('articleassessment-pleaserate'),feedback=$.ArticleAssessment.fn.getMsg('articleassessment-featurefeedback'),yourfeedback=$.ArticleAssessment.fn.getMsg('articleassessment-yourfeedback'),articlerating=$.ArticleAssessment.fn.getMsg('articleassessment-articlerating'),resultshide=$.ArticleAssessment.fn.getMsg('articleassessment-results-hide'),resultsshow=$.ArticleAssessment.fn.getMsg('articleassessment-results-show');submitbutton=$.ArticleAssessment.fn.getMsg('articleassessment-submit');$structure.find('#article-assessment-rate legend').text(yourfeedback).end().find('.article-assessment-rate-instructions').text(instructions).end().find('.article-assessment-rate-feedback').html(feedback).find('.feedbacklink').wrap('<a href="#"></a>').parent().click($.ArticleAssessment.fn.showFeedback).end().end().end().find('#article-assessment-ratings legend').text(articlerating).end().find('.article-assessment-show-ratings').html(resultsshow).find('.showlink').wrap('<a href="#"></a>').parent().click($.ArticleAssessment.fn.showRatings).end().end().end().find('.article-assessment-hide-ratings').html(resultshide).find('.hidelink').wrap('<a href="#"></a>').parent().click($.ArticleAssessment.fn.hideRatings).end().end().end().find('.article-assessment-submit input').attr("value",submitbutton).end();if($.cookie('mwArticleAssessmentHideFeedback')){$structure.find('.article-assessment-rate-feedback').hide();}
 44+for(var i=0;i<settings.fieldMessages.length;i++){var $field=$(settings.fieldHTML),$rating=$(settings.ratingHTML),label=$.ArticleAssessment.fn.getMsg(settings.fieldPrefix+settings.fieldMessages[i]),field=settings.fieldMessages[i],hint=$.ArticleAssessment.fn.getMsg(settings.fieldPrefix+settings.fieldMessages[i]+settings.fieldHintSuffix),count=$.ArticleAssessment.fn.getMsg('articleassessment-noratings',[0,0]);$field.attr('id','articleassessment-rate-'+field).find('label').attr('for','rating_'+field).attr('original-title',hint).text(label).end().find('select').attr('id','rating_'+field).attr('name','rating['+field+']');$rating.attr('id','articleassessment-rating-'+field).find('.article-assessment-rating-field-name').text(label).end().find('.article-assessment-rating-count').text(count);$structure.find('.article-assessment-rating-fields').append($field).end().find('#article-assessment-ratings').append($rating);}
 45+$structure.find('#article-assessment').data('articleAssessment-context',{'settings':settings,'config':config});$('#catlinks').before($structure);$.ArticleAssessment.fn.hideRatings();if($('#article-assessment-rate').height()>$('#article-assessment-ratings').height()){$('#article-assessment-ratings').css('minHeight',$('#article-assessment-rate').height());}else{$('#article-assessment-rate').css('minHeight',$('#article-assessment-ratings').height());}
 46+$.ArticleAssessment.fn.getRatingData();$('.rating-field').each(function(){$(this).wrapAll('<div class="rating-field"></div>').parent().stars({inputType:'select',callback:function(value,link){value.$stars.each(function(){$(this).removeClass('ui-stars-star-stale').removeClass('ui-stars-star-rated');$('#article-assessment input:disabled').removeAttr('disabled');});}});});$('.field-wrapper label[original-title]').each(function(){$(this).after($('<span class="rating-field-hint" />').attr('original-title',$(this).attr('original-title')).tipsy({gravity:'se',opacity:'0.9'}));});$('#article-assessment').submit(function(){$.ArticleAssessment.fn.submitRating();return false;});$('#article-assessment input[type=submit]').attr('disabled','disabled');},'showRatings':function(){$('#article-assessment-ratings').removeClass('article-assessment-ratings-disabled').find('.article-assessment-show-ratings').hide().end().find('.article-assessment-hide-ratings').show();return false;},'hideRatings':function(){$('#article-assessment-ratings').addClass('article-assessment-ratings-disabled').find('.article-assessment-hide-ratings').hide().end().find('.article-assessment-show-ratings').show();return false;},'getRatingData':function(){var config=$('#article-assessment').data('articleAssessment-context').config;var requestData={'action':'query','list':'articleassessment','aapageid':config.pageID,'aauserrating':1,'format':'json'}
 47+if(config.userID.length==32){requestData.aaanontoken=config.userID;}
 48+var request=$.ajax({url:wgScriptPath+'/api.php',data:requestData,dataType:'json',success:$.ArticleAssessment.fn.afterGetRatingData,error:function(XMLHttpRequest,textStatus,errorThrown){$.ArticleAssessment.fn.flashNotice($.ArticleAssessment.fn.getMsg('articleassessment-error'),{'class':'article-assessment-error-msg'});}});},'afterGetRatingData':function(data){var settings=$('#article-assessment').data('articleAssessment-context').settings;if(data.query&&data.query.articleassessment&&data.query.articleassessment.length>0){for(var r in data.query.articleassessment[0].ratings){var rating=data.query.articleassessment[0].ratings[r],$rating=$('#'+rating.ratingdesc),count=rating.count,total=(rating.total/count).toFixed(1),label=$.ArticleAssessment.fn.getMsg('articleassessment-noratings',[total,count]);$rating.find('.article-assessment-rating-field-value').text(total).end().find('.article-assessment-rating-count').text(label);if(rating.userrating){var $rateControl=$('#'+rating.ratingdesc.replace('rating','rate')+' .rating-field');$rateControl.stars('select',rating.userrating);$.ArticleAssessment.fn.showRatings();}}
 49+if(typeof data.query.articleassessment[0].stale!='undefined'){$('.ui-stars-star-on').addClass('ui-stars-star-stale');var msg=$.ArticleAssessment.fn.getMsg('articleassessment-stalemessage-norevisioncount');$.ArticleAssessment.fn.flashNotice(msg,{'class':'article-assessment-stale-msg'});}else{$('.ui-stars-star-on').addClass('ui-stars-star-rated');}}
 50+$('.article-assessment-rating-field-value').each(function(){$(this).css({'width':120-(120*(parseFloat($(this).text())/5))+'px'})});},'submitRating':function(){var config=$('#article-assessment').data('articleAssessment-context').config;$.ArticleAssessment.fn.flashNotice();$('.rating-field').stars('disable');$('#article-assessment input').attr('disabled','disabled');var results={};$('.rating-field input').each(function(){var fieldName=$(this).attr('name').match(/\[([a-zA-Z0-9\-]*)\]/)[1];results[fieldName]=$(this).val();});var request=$.ajax({url:wgScriptPath+'/api.php',type:'POST',data:{'action':'articleassessment','revid':config.revID,'pageid':config.pageID,'r1':results['wellsourced'],'r2':results['neutrality'],'r3':results['completeness'],'r4':results['readability'],'anontoken':config.userID,'format':'json'},dataType:'json',success:$.ArticleAssessment.fn.afterSubmitRating,error:function(XMLHttpRequest,textStatus,errorThrown){$.ArticleAssessment.fn.flashNotice($.ArticleAssessment.fn.getMsg('articleassessment-error'),{'class':'article-assessment-error-msg'});}});},'afterSubmitRating':function(data){$.ArticleAssessment.fn.getRatingData();$('.ui-stars-star-on').addClass('ui-stars-star-rated');$('.rating-field').stars('enable');$('#article-assessment input:disabled').removeAttr('disabled');$.ArticleAssessment.fn.showRatings();$.ArticleAssessment.fn.flashNotice($.ArticleAssessment.fn.getMsg('articleassessment-thanks'),{'class':'article-assessment-success-msg'});},'flashNotice':function(text,options){if(arguments.length==0){$('#article-assessment .article-assessment-flash').remove();}else{$('#article-assessment .article-assessment-flash').remove();var className=options['class'];$msg=$('<div />').addClass('article-assessment-flash').html(text);if(options['class']){$msg.addClass(options['class']);}
 51+$('#article-assessment .article-assessment-submit').append($msg);}},'showFeedback':function(){$.ArticleAssessment.fn.withJUI(function(){var $dialogDiv=$('#article-assessment-dialog');if($dialogDiv.size()==0){$dialogDiv=$('<div id="article-assessment-dialog" class="loading" />').dialog({width:600,height:400,bgiframe:true,autoOpen:true,modal:true,title:$.ArticleAssessment.fn.getMsg('articleassessment-survey-title'),close:function(){$(this).dialog('option','height',400).find('.article-assessment-success-msg, .article-assessment-error-msg').remove().end().find('form').show();}});$dialogDiv.load(wgScript+'?title=Special:SimpleSurvey&survey=articlerating&raw=1',function(){$(this).find('form').bind('submit',$.ArticleAssessment.fn.submitFeedback);$(this).removeClass('loading');});}
 52+$dialogDiv.dialog('open');});return false;},'submitFeedback':function(){var $dialogDiv=$('#article-assessment-dialog');$dialogDiv.find('form').hide().end().addClass('loading');var formData={};$dialogDiv.find('input').each(function(){var name=$(this).attr('name');if(name!==''){if(name.substr(-2)=='[]'){var trimmedName=name.substr(0,name.length-2);if(typeof formData[trimmedName]=='undefined'){formData[trimmedName]=[];}
 53+formData[trimmedName].push($(this).val());}else{formData[name]=$(this).val();}}});formData.title='Special:SimpleSurvey';$.ajax({url:wgScript,type:'POST',data:formData,dataType:'html',success:function(data){var success=$(data).find('.simplesurvey-success').size()>0;var $msgDiv=$('<div />').addClass(success?'article-assessment-success-msg':'article-assessment-error-msg').text($.ArticleAssessment.fn.getMsg(success?'articleassessment-survey-thanks':'articleassessment-error')).appendTo($dialogDiv);$dialogDiv.dialog('option','height',$msgDiv.height()+100).removeClass('loading');if(success){$('#article-assessment .article-assessment-rate-feedback').hide();$.cookie('mwArticleAssessmentHideFeedback',true,{'expires':30,'path':'/'});}},error:function(XMLHttpRequest,textStatus,errorThrown){var $msgDiv=$('<div />').addClass('article-assessment-error-msg').text($.ArticleAssessment.fn.getMsg('articleassessment-error')).appendTo($dialogDiv);$dialogDiv.dialog('option','height',$msgDiv.height()+100).removeClass('loading');}});return false;},'addMessages':function(messages){for(var key in messages){$.ArticleAssessment.messages[key]=messages[key];}},'getMsg':function(key,args){if(!(key in $.ArticleAssessment.messages)){return'['+key+']';}
 54+var msg=$.ArticleAssessment.messages[key];if(typeof args=='object'||typeof args=='array'){for(var i=0;i<args.length;i++){msg=msg.replace(new RegExp('\\$'+(parseInt(i)+1),'g'),args[i]);}}else if(typeof args=='string'||typeof args=='number'){msg=msg.replace(/\$1/g,args);}
 55+return msg;},'withJUI':function(callback){if(typeof $.ui=='undefined'){$.getScript(wgArticleAssessmentJUIPath,callback);}else{callback();}}}};$(document).ready(function(){$.ArticleAssessment.fn.init();});})(jQuery);jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};if(value===null){value='';options.expires=-1;}
 56+var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toUTCString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;}
 57+expires='; expires='+date.toUTCString();}
 58+var path=options.path?'; path='+(options.path):'';var domain=options.domain?'; domain='+(options.domain):'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i]);if(cookie.substring(0,name.length+1)==(name+'=')){cookieValue=decodeURIComponent(cookie.substring(name.length+1));break;}}}
 59+return cookieValue;}};(function($){$.stars={defaults:{inputType:'radio',split:0,disabled:false,cancelTitle:'Cancel Rating',cancelValue:0,cancelShow:true,disableValue:true,oneVoteOnly:false,showTitles:false,captionEl:null,callback:null,starWidth:16,cancelClass:'ui-stars-cancel',starClass:'ui-stars-star',starOnClass:'ui-stars-star-on',starHoverClass:'ui-stars-star-hover',starDisabledClass:'ui-stars-star-disabled',cancelHoverClass:'ui-stars-cancel-hover',cancelDisabledClass:'ui-stars-cancel-disabled'},create:function(){var self=this,o=this.options,starId=0;this.element.data('former.stars',this.element.html());o.isSelect=o.inputType=='select';this.$form=$(this.element).closest('form');this.$selec=o.isSelect?$('select',this.element):null;this.$rboxs=o.isSelect?$('option',this.$selec):$(':radio',this.element);this.$stars=this.$rboxs.map(function(i)
 60+{var el={value:this.value,title:(o.isSelect?this.text:this.title)||this.value,isDefault:(o.isSelect&&this.defaultSelected)||this.defaultChecked};if(i==0){o.split=typeof o.split!='number'?0:o.split;o.val2id=[];o.id2val=[];o.id2title=[];o.name=o.isSelect?self.$selec.get(0).name:this.name;o.disabled=o.disabled||(o.isSelect?$(self.$selec).attr('disabled'):$(this).attr('disabled'));}
 61+if(el.value==o.cancelValue){o.cancelTitle=el.title;return null;}
 62+o.val2id[el.value]=starId;o.id2val[starId]=el.value;o.id2title[starId]=el.title;if(el.isDefault){o.checked=starId;o.value=o.defaultValue=el.value;o.title=el.title;}
 63+var $s=$('<div/>').addClass(o.starClass);var $a=$('<a/>').attr('title',o.showTitles?el.title:'').text(el.value);if(o.split){var oddeven=(starId%o.split);var stwidth=Math.floor(o.starWidth/o.split);$s.width(stwidth);$a.css('margin-left','-'+(oddeven*stwidth)+'px');}
 64+starId++;return $s.append($a).get(0);});o.items=starId;o.isSelect?this.$selec.remove():this.$rboxs.remove();this.$cancel=$('<div/>').addClass(o.cancelClass).append($('<a/>').attr('title',o.showTitles?o.cancelTitle:'').text(o.cancelValue));o.cancelShow&=!o.disabled&&!o.oneVoteOnly;o.cancelShow&&this.element.append(this.$cancel);this.element.append(this.$stars);if(o.checked===undefined){o.checked=-1;o.value=o.defaultValue=o.cancelValue;o.title='';}
 65+this.$value=$("<input type='hidden' name='"+o.name+"' value='"+o.value+"' />");this.element.append(this.$value);this.$stars.bind('click.stars',function(e){if(!o.forceSelect&&o.disabled)return false;var i=self.$stars.index(this);o.checked=i;o.value=o.id2val[i];o.title=o.id2title[i];self.$value.attr({disabled:o.disabled?'disabled':'',value:o.value});fillTo(i,false);self.disableCancel();!o.forceSelect&&self.callback(e,'star');}).bind('mouseover.stars',function(){if(o.disabled)return false;var i=self.$stars.index(this);fillTo(i,true);}).bind('mouseout.stars',function(){if(o.disabled)return false;fillTo(self.options.checked,false);});this.$cancel.bind('click.stars',function(e){if(!o.forceSelect&&(o.disabled||o.value==o.cancelValue))return false;o.checked=-1;o.value=o.cancelValue;o.title='';self.$value.val(o.value);o.disableValue&&self.$value.attr({disabled:'disabled'});fillNone();self.disableCancel();!o.forceSelect&&self.callback(e,'cancel');}).bind('mouseover.stars',function(){if(self.disableCancel())return false;self.$cancel.addClass(o.cancelHoverClass);fillNone();self.showCap(o.cancelTitle);}).bind('mouseout.stars',function(){if(self.disableCancel())return false;self.$cancel.removeClass(o.cancelHoverClass);self.$stars.triggerHandler('mouseout.stars');});this.$form.bind('reset.stars',function(){!o.disabled&&self.select(o.defaultValue);});$(window).unload(function(){self.$cancel.unbind('.stars');self.$stars.unbind('.stars');self.$form.unbind('.stars');self.$selec=self.$rboxs=self.$stars=self.$value=self.$cancel=self.$form=null;});function fillTo(index,hover){if(index!=-1){var addClass=hover?o.starHoverClass:o.starOnClass;var remClass=hover?o.starOnClass:o.starHoverClass;self.$stars.eq(index).prevAll('.'+o.starClass).andSelf().removeClass(remClass).addClass(addClass);self.$stars.eq(index).nextAll('.'+o.starClass).removeClass(o.starHoverClass+' '+o.starOnClass);self.showCap(o.id2title[index]);}
 66+else fillNone();};function fillNone(){self.$stars.removeClass(o.starOnClass+' '+o.starHoverClass);self.showCap('');};this.select(o.value);o.disabled&&this.disable();},disableCancel:function(){var o=this.options,disabled=o.disabled||o.oneVoteOnly||(o.value==o.cancelValue);if(disabled)this.$cancel.removeClass(o.cancelHoverClass).addClass(o.cancelDisabledClass);else this.$cancel.removeClass(o.cancelDisabledClass);this.$cancel.css('opacity',disabled?0.5:1);return disabled;},disableAll:function(){var o=this.options;this.disableCancel();if(o.disabled)this.$stars.filter('div').addClass(o.starDisabledClass);else this.$stars.filter('div').removeClass(o.starDisabledClass);},showCap:function(s){var o=this.options;if(o.captionEl)o.captionEl.text(s);},value:function(){return this.options.value;},select:function(val){var o=this.options,e=(val==o.cancelValue)?this.$cancel:this.$stars.eq(o.val2id[val]);o.forceSelect=true;e.triggerHandler('click.stars');o.forceSelect=false;},selectID:function(id){var o=this.options,e=(id==-1)?this.$cancel:this.$stars.eq(id);o.forceSelect=true;e.triggerHandler('click.stars');o.forceSelect=false;},enable:function(){this.options.disabled=false;this.disableAll();},disable:function(){this.options.disabled=true;this.disableAll();},destroy:function(){this.$form.unbind('.stars');this.$cancel.unbind('.stars').remove();this.$stars.unbind('.stars').remove();this.$value.remove();this.element.unbind('.stars').html(this.element.data('former.stars')).removeData('stars');return this;},callback:function(e,type){var o=this.options;o.callback&&o.callback(this,type,o.value,e);o.oneVoteOnly&&!o.disabled&&this.disable();}}
 67+$.fn.stars=function(){var args=Array.prototype.slice.call(arguments);var out=$(this);$(this).each(function(){var context=$(this).data('stars-context');if(typeof context=='undefined'||context==null){context=$.extend({},{element:$(this),options:$.stars.defaults},$.stars);}
 68+if(args.length>0){if(typeof args[0]=='object'){context.options=$.extend({},context.options,args[0]);$.stars.create.call(context);}else if(typeof args[0]=='string'){var funcName=args[0];out=$.stars[funcName].call(context,args.slice(1))||out;}}else{$.stars.create.call(context);}
 69+$(this).data('stars-context',context);});return out;};})(jQuery);(function($){function Tipsy(element,options){this.$element=$(element);this.options=options;this.enabled=true;this.fixTitle();}
 70+Tipsy.prototype={show:function(){var title=this.getTitle();if(title&&this.enabled){var $tip=this.tip();$tip.find('.tipsy-inner')[this.options.html?'html':'text'](title);$tip[0].className='tipsy';$tip.remove().css({top:0,left:0,visibility:'hidden',display:'block'}).appendTo(document.body);var pos=$.extend({},this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight});var actualWidth=$tip[0].offsetWidth,actualHeight=$tip[0].offsetHeight;var gravity=(typeof this.options.gravity=='function')?this.options.gravity.call(this.$element[0]):this.options.gravity;var tp;switch(gravity.charAt(0)){case'n':tp={top:pos.top+pos.height+this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case's':tp={top:pos.top-actualHeight-this.options.offset,left:pos.left+pos.width/2-actualWidth/2};break;case'e':tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left-actualWidth-this.options.offset};break;case'w':tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left+pos.width+this.options.offset};break;}
 71+if(gravity.length==2){if(gravity.charAt(1)=='w'){tp.left=pos.left+pos.width/2-15;}else{tp.left=pos.left+pos.width/2-actualWidth+15;}}
 72+$tip.css(tp).addClass('tipsy-'+gravity);if(this.options.fade){$tip.stop().css({opacity:0,display:'block',visibility:'visible'}).animate({opacity:this.options.opacity});}else{$tip.css({visibility:'visible',opacity:this.options.opacity});}}},hide:function(){if(this.options.fade){this.tip().stop().fadeOut(function(){$(this).remove();});}else{this.tip().remove();}},fixTitle:function(){var $e=this.$element;if($e.attr('title')||typeof($e.attr('original-title'))!='string'){$e.attr('original-title',$e.attr('title')||'').removeAttr('title');}},getTitle:function(){var title,$e=this.$element,o=this.options;this.fixTitle();var title,o=this.options;if(typeof o.title=='string'){title=$e.attr(o.title=='title'?'original-title':o.title);}else if(typeof o.title=='function'){title=o.title.call($e[0]);}
 73+title=(''+title).replace(/(^\s*|\s*$)/,"");return title||o.fallback;},tip:function(){if(!this.$tip){this.$tip=$('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');}
 74+return this.$tip;},validate:function(){if(!this.$element[0].parentNode){this.hide();this.$element=null;this.options=null;}},enable:function(){this.enabled=true;},disable:function(){this.enabled=false;},toggleEnabled:function(){this.enabled=!this.enabled;}};$.fn.tipsy=function(options){if(options===true){return this.data('tipsy');}else if(typeof options=='string'){var tipsy=this.data('tipsy');if(tipsy)tipsy[options]();return this;}
 75+options=$.extend({},$.fn.tipsy.defaults,options);function get(ele){var tipsy=$.data(ele,'tipsy');if(!tipsy){tipsy=new Tipsy(ele,$.fn.tipsy.elementOptions(ele,options));$.data(ele,'tipsy',tipsy);}
 76+return tipsy;}
 77+function enter(){var tipsy=get(this);tipsy.hoverState='in';if(options.delayIn==0){tipsy.show();}else{tipsy.fixTitle();setTimeout(function(){if(tipsy.hoverState=='in')tipsy.show();},options.delayIn);}};function leave(){var tipsy=get(this);tipsy.hoverState='out';if(options.delayOut==0){tipsy.hide();}else{setTimeout(function(){if(tipsy.hoverState=='out')tipsy.hide();},options.delayOut);}};if(!options.live)this.each(function(){get(this);});if(options.trigger!='manual'){var binder=options.live?'live':'bind',eventIn=options.trigger=='hover'?'mouseenter':'focus',eventOut=options.trigger=='hover'?'mouseleave':'blur';this[binder](eventIn,enter)[binder](eventOut,leave);}
 78+return this;};$.fn.tipsy.defaults={delayIn:0,delayOut:0,fade:false,fallback:'',gravity:'n',html:false,live:false,offset:0,opacity:0.8,title:'title',trigger:'hover'};$.fn.tipsy.elementOptions=function(ele,options){return $.metadata?$.extend({},options,$(ele).metadata()):options;};$.fn.tipsy.autoNS=function(){return $(this).offset().top>($(document).scrollTop()+$(window).height()/2)?'s':'n';};$.fn.tipsy.autoWE=function(){return $(this).offset().left>($(document).scrollLeft()+$(window).width()/2)?'e':'w';};})(jQuery);
\ No newline at end of file
Index: trunk/extensions/ArticleAssessmentPilot/Makefile
@@ -0,0 +1,39 @@
 2+#
 3+# Handy makefile to combine and minify css and javascript files
 4+#
 5+SHELL := /bin/bash
 6+
 7+JS := \
 8+ js/ArticleAssessment.js\
 9+ js/jquery.cookie.js\
 10+ js/jquery.stars.js\
 11+ js/jquery.tipsy.js
 12+
 13+all: \
 14+ js/ArticleAssessment.combined.min.js
 15+
 16+
 17+# JavaScript Combination
 18+js/ArticleAssessment.combined.js: $(JS)
 19+ cat $(JS) > js/ArticleAssessment.combined.js
 20+
 21+# JavaScript Minification
 22+
 23+js/ArticleAssessment.combined.min.js: js/ArticleAssessment.combined.js jsmin
 24+ if [ -e ./jsmin ]; then ./jsmin < js/ArticleAssessment.combined.js > js/ArticleAssessment.combined.min.js;\
 25+ else jsmin < js/ArticleAssessment.combined.js > js/ArticleAssessment.combined.min.js; fi
 26+
 27+# JSMin - For more info on JSMin, see: http://www.crockford.com/javascript/jsmin.html
 28+
 29+jsmin:
 30+ type -P jsmin &>/dev/null || ( wget http://www.crockford.com/javascript/jsmin.c; gcc jsmin.c -o jsmin )
 31+
 32+# Actions
 33+
 34+distclean: clean
 35+ rm -rf jsmin
 36+ rm -rf jsmin.c
 37+
 38+clean:
 39+ rm -f js/ArticleAssessment.combined.*
 40+

Status & tagging log