Skip to content

Added util.Notifier #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 16, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": true,
"newcap": true,
"supernew": true,
"shadow": true,
"noarg": true,
"noempty": true,
"nonew": true,
"quotmark": "single",
"trailing": true,
"undef": true,
"unused": "vars",
"laxbreak": true,
"laxcomma": false,
"onevar": false,
"bitwise": false,
"forin": false,
"regexp": false,
"strict": true,
"scripturl": true,

// Environment
"browser": true,

// Globals
"predef": [
"dataValues",
"jQuery",
"mediaWiki",
"QUnit",
"util"
]
}
14 changes: 12 additions & 2 deletions ValueView.resources.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,18 @@
'jquery.valueview/valueview.css',
),
'dependencies' => array(
'dataValues.DataValue',
'jquery.ui.widget',
'jquery.valueview.base',
'jquery.valueview.ViewState',
'jquery.valueview.experts', // because vv deals with ExpertFactory
'jquery.valueview.experts.unsupportedvalue', // for displaying unsupported values
'jquery.valueview.experts.emptyvalue', // for displaying empty values
// NOTE: don't add further experts here unless they are required by the valueview
// widget directly. All experts are supposed to be loaded separately, by demand and
// widget directly. All experts are supposed to be loaded separately, by demand and
// by the controller requiring them.
'util.Notifier',
'valueParsers.ValueParser',
),
),

Expand All @@ -102,6 +105,7 @@
'dataValues.util',
'dataValues.values',
'dataTypes',
'util.Notifier',
),
),

Expand Down Expand Up @@ -298,7 +302,13 @@
'valueview-preview-label',
'valueview-preview-novalue',
),
)
),

'util.Notifier' => $moduleTemplate + array(
'scripts' => array(
'../lib/util.Notifier.js',
),
),

);

Expand Down
10 changes: 10 additions & 0 deletions ValueView.tests.qunit.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
'jquery.valueview.experts',
'jquery.valueview.MockViewState',
'qunit.parameterize',
'util.Notifier',
),
),

Expand Down Expand Up @@ -232,6 +233,15 @@
),
),

'util.Notifier.tests' => array(
'scripts' => array(
"$bp/lib/util.Notifier.tests.js",
),
'dependencies' => array(
'util.Notifier',
),
)

);

} );
Expand Down
117 changes: 117 additions & 0 deletions lib/util.Notifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* @licence GNU GPL v2+
* @author Daniel Werner < daniel.werner@wikimedia.de >
*/

var util = util || {};

util.Notifier = ( function() {
'use strict';

/**
* Tracks the current notifications for the Notifier's current function.
* @type {Array}
*/
var currentNotifications = [];

/**
* Constructor to create an object which takes several callbacks in its constructor. Each
* callback is mapped to a keyword. The keyword can be used in a "notify" function which will
* then trigger the callback. The notification object itself is immutable. The object will only
* hold a reference to the given map though and won't copy the map, so if the map changes from
* the outside, the notifier will also be affected.
* Instantiation also works without using the "new" keyword.
*
* @example
* <code>
* var notifier = util.Notifier( {
* valid: function() { this.current() },
* invalid: function() { this.current() }
* } );
* notifier.notify( 'valid' ); // will alert 'valid'
* notifier.notify( 'invalid' ); // will alert 'invalid'
* notifier.notify( 'whatever' ); // Nothing happens, no notification registered for this one.
* </code>
*
* @constructor
* @since 0.1
*
* @param {Object} [notificationMap={}] Map from notification IDs to callback functions. The
* context of the functions when called by notify() is Notifier instance.
*/
var SELF = function Notifier( notificationMap ) {
// allow instance without "new":
if( !( this instanceof SELF ) ) {
return new SELF( notificationMap );
}

if( !notificationMap ) {
notificationMap = {};
} else if( typeof notificationMap !== 'object' ) {
throw new Error( 'Notifier requires a notification map in form of an object' );
}

/**
* Will trigger a callback related to a given notification string if there is a callback
* function defined for that string.
*
* @since 0.1
*
* @param {string} notification
* @param [args=[]] array Optional arguments that will be provided to the callback.
* @return {boolean} Whether a notification has been sent. false if the notification has not
* been registered in the constructor.
*/
this.notify = function( notification, args ) {
var notifyCallback = notificationMap[ notification ];

if( !notifyCallback ) {
return false;
}

// for this.current() to keep track over current notifications
currentNotifications.push( notification );

// context of the callback will be the Notifier instance
notifyCallback.apply( this, args || [] );

// NOTE: the above might fail with an error. If there is a try outside the notify() call,
// the currentNotifications won't be updated after the error got triggered. Putting
// the above in a try would be annoying for debugging. currentNotifications having
// remnants doesn't have any side-effects as long as we don't use it as an implication
// for whether we are inside of a notification right now.

currentNotifications.pop();
return true;
};

/**
* Returns whether the Notifier actually has a listener for a certain notification topic.
* false if no callback is registered for the notification subject.
*
* @since 0.1
*
* @param {string} notification
* @returns boolean
*/
this.hasListenerFor = function( notification ) {
return !!notificationMap[ notification ];
};

/**
* Returns what is currently being notified. Will only return a value when used within a
* callback because only within callbacks things are being notified.
*
* @since 0.1
*
* @returns string|null
*/
this.current = function() {
var current = currentNotifications[ currentNotifications.length - 1 ];
return current !== undefined ? current : null;
};
};

return SELF;

}() );
12 changes: 6 additions & 6 deletions resources/jquery.valueview/valueview.Expert.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @licence GNU GPL v2+
* @author Daniel Werner < daniel.werner@wikimedia.de >
*/
( function( dv, $, vv ) {
( function( dv, $, vv, util ) {
'use strict';

/**
Expand Down Expand Up @@ -59,7 +59,7 @@
*
* @param {HTMLElement|jQuery} viewPortNode
* @param {jQuery.valueview.ViewState} relatedViewState
* @param {dv.util.Notifier} [valueViewNotifier] Required so the expert can notify the valueview
* @param {util.Notifier} [valueViewNotifier] Required so the expert can notify the valueview
* about certain events. The following notification keys can be used:
* - change: will be sent when raw value displayed by the expert changes. Either by a
* user action or by calling the rawValue() method. First parameter is a
Expand All @@ -79,9 +79,9 @@
}

if( !valueViewNotifier ) {
valueViewNotifier = dv.util.Notifier();
valueViewNotifier = util.Notifier();
}
else if( !( valueViewNotifier instanceof dv.util.Notifier ) ) {
else if( !( valueViewNotifier instanceof util.Notifier ) ) {
throw new Error( 'No Notifier object was provided to the valueview expert' );
}

Expand Down Expand Up @@ -134,7 +134,7 @@

/**
* Object for publishing changes to the outside.
* @type dv.util.Notifier
* @type util.Notifier
*/
_viewNotifier: null,

Expand Down Expand Up @@ -354,4 +354,4 @@
blur: function() {}
};

}( dataValues, jQuery, jQuery.valueview ) );
}( dataValues, jQuery, jQuery.valueview, util ) );
8 changes: 4 additions & 4 deletions resources/jquery.valueview/valueview.valueview.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @licence GNU GPL v2+
* @author Daniel Werner < daniel.werner@wikimedia.de >
*/
( function( dv, dt, vp, $ ) {
( function( dv, dt, vp, util, $ ) {
'use strict';

var PARENT = $.Widget;
Expand Down Expand Up @@ -800,12 +800,12 @@
*
* @since 0.1
*
* @return dataValues.util.Notifier
* @return {util.Notifier}
*/
viewNotifier: function() {
var self = this;

return new dv.util.Notifier( {
return new util.Notifier( {
change: function() {
if( !self._expert ) {
// someone notified about change while there couldn't have been one since there
Expand Down Expand Up @@ -846,4 +846,4 @@
}
} );

}( dataValues, dataTypes, valueParsers, jQuery ) );
}( dataValues, dataTypes, valueParsers, util, jQuery ) );
2 changes: 1 addition & 1 deletion tests/qunit/jquery.valueview/valueview.tests.testExpert.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,4 @@ testExpert.verifyTestDefinition = function( testDefinition ) {

return testExpert; // expose

}( jQuery, QUnit, jQuery.valueview, dataValues.util.Notifier ) );
}( jQuery, QUnit, jQuery.valueview, util.Notifier ) );
Loading