Bmansurov has uploaded a new change for review.
https://gerrit.wikimedia.org/r/249991
Change subject: Add models and view event handlers
......................................................................
Add models and view event handlers
Both Card and CardList views have an $el property which can be inserted
into the DOM.
Now the gateway returns a list of cards whose HTML can be retrieved
using the following code:
```
mw.loader.using( 'ext.cards.gateway' ).done( function() {
var gateway = new mw.cards.CardsGateway( { api: new mw.Api() } );
// '1' and '2' are page titles, while 200 is the desired thumbnail width
gateway.getCards( ['1', '2'], 200 ).done( function( cards ) {
// insert cards.$el to the DOM
} );
} );
```
Other changes:
* View can re-render itself if its model changes.
* View can listen to DOM events.
Change-Id: Ie110c1d96000fa9ec1ccb3029e84b7ad035d528f
---
M extension.json
M resources/CardsGateway.js
M resources/init.js
A resources/models/Card.js
D resources/view/Card.js
D resources/view/CardList.js
D resources/view/cards.hogan
A resources/views/Card.js
A resources/views/CardList.js
R resources/views/card.hogan
A resources/views/cards.hogan
R resources/views/noimage.png
R resources/views/noimage.svg
R resources/views/styles.less
14 files changed, 195 insertions(+), 88 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Cards
refs/changes/91/249991/1
diff --git a/extension.json b/extension.json
index 07f3c88..f13d64d 100644
--- a/extension.json
+++ b/extension.json
@@ -32,7 +32,16 @@
"resources/init.js"
]
},
- "ext.cards.view": {
+ "ext.cards.models": {
+ "targets": [
+ "mobile"
+ ],
+ "group": "other",
+ "scripts": [
+ "resources/models/Card.js"
+ ]
+ },
+ "ext.cards.views": {
"targets": [
"mobile"
],
@@ -44,15 +53,15 @@
"ext.cards.init"
],
"scripts": [
- "resources/view/Card.js",
- "resources/view/CardList.js"
+ "resources/views/Card.js",
+ "resources/views/CardList.js"
],
"styles": [
- "resources/view/styles.less"
+ "resources/views/styles.less"
],
"templates": {
- "card.hogan": "resources/view/card.hogan",
- "cards.hogan": "resources/view/cards.hogan"
+ "card.hogan": "resources/views/card.hogan",
+ "cards.hogan": "resources/views/cards.hogan"
}
},
"ext.cards.gateway": {
@@ -61,7 +70,8 @@
],
"group": "other",
"dependencies": [
- "ext.cards.view"
+ "ext.cards.models",
+ "ext.cards.views"
],
"scripts": [
"resources/CardsGateway.js"
diff --git a/resources/CardsGateway.js b/resources/CardsGateway.js
index 44c6963..3daf8dc 100644
--- a/resources/CardsGateway.js
+++ b/resources/CardsGateway.js
@@ -6,8 +6,9 @@
* @readonly
*/
var THUMB_WIDTH = 50,
- Card = mw.cards.Card,
- CardList = mw.cards.CardList;
+ CardModel = mw.cards.models.Card,
+ CardView = mw.cards.views.Card,
+ CardListView = mw.cards.views.CardList;
/**
* Gateway for retrieving article info from the API and returning
{mw.cards.CardList}
@@ -27,15 +28,15 @@
* @param {String[]} articleTitles array of article titles
* @param {Number} [thumbWidth] Thumbnail width in pixels. Defaults to
* {@link THUMB_WIDTH}
- * @return {jQuery.Deferred} the result resolves with
{mw.cards.CardList}
+ * @return {jQuery.Deferred} the result resolves with
{mw.cards.views.CardList}
*/
mw.cards.CardsGateway.prototype.getCards = function ( articleTitles,
thumbWidth ) {
var article,
- cards = [],
+ cardViews = [],
result = $.Deferred();
if ( !articleTitles.length ) {
- result.resolve( new CardList( cards ) );
+ result.resolve( new CardListView( cardViews ) );
return result;
}
@@ -51,7 +52,7 @@
continue: ''
} ).done( function ( data ) {
if ( data.query && data.query.pages ) {
- cards = $.map( data.query.pages, function (
page ) {
+ cardViews = $.map( data.query.pages, function (
page ) {
article = {
title: page.title,
url: ( new mw.Title( page.title
) ).getUrl(),
@@ -68,12 +69,12 @@
article.extract = page.extract;
}
- return new Card( article );
+ return new CardView( new CardModel(
article ) );
} );
}
- result.resolve( new CardList( cards ) );
+ result.resolve( new CardListView( cardViews ) );
} ).fail( function () {
- result.resolve( new CardList( cards ) );
+ result.resolve( new CardListView( cardViews ) );
} );
return result;
diff --git a/resources/init.js b/resources/init.js
index b552931..e706610 100644
--- a/resources/init.js
+++ b/resources/init.js
@@ -5,5 +5,8 @@
* @class mw.cards
* @singleton
*/
- mw.cards = {};
+ mw.cards = {
+ models: {},
+ views: {}
+ };
} )();
diff --git a/resources/models/Card.js b/resources/models/Card.js
new file mode 100644
index 0000000..e068619
--- /dev/null
+++ b/resources/models/Card.js
@@ -0,0 +1,43 @@
+( function () {
+ 'use strict';
+
+ var CardModel;
+
+ /**
+ * Model that handles a single article info
+ *
+ * @class
+ * @inherits OO.EventEmitter
+ * @param {Object} data article information, such as title, url, etc.
about an article
+ */
+ CardModel = function ( data ) {
+ CardModel.super.apply( this, arguments );
+ this.data = data;
+ };
+ OO.inheritClass( CardModel, OO.EventEmitter );
+
+ /**
+ * Set a model property
+ * Emits a 'changed' event with the object whose key is the property
that's being updated
+ * and value is the value that's being set.
+ *
+ * @param {String} key
+ * @param {Mixed} value
+ */
+ CardModel.prototype.set = function ( key, value ) {
+ this.data[ key ] = value;
+ this.emit( 'change', { key: value } );
+ };
+
+ /**
+ * Get the model property's value
+ *
+ * @param key property that's being looked up
+ * @returns {Mixed}
+ */
+ CardModel.prototype.get = function ( key ) {
+ return this.data[ key ];
+ };
+
+ mw.cards.models.Card = CardModel;
+} )();
diff --git a/resources/view/Card.js b/resources/view/Card.js
deleted file mode 100644
index 3407b82..0000000
--- a/resources/view/Card.js
+++ /dev/null
@@ -1,26 +0,0 @@
-( function () {
- 'use strict';
-
- /**
- * View that handles a single article info
- *
- * @class
- * @param {Object} article information, such as title, url, etc. about
an article
- */
- mw.cards.Card = function ( article ) {
- this.article = article;
- };
-
- OO.initClass( mw.cards.Card );
-
- mw.cards.Card.prototype.template = mw.template.get( 'ext.cards.view',
'card.hogan' );
-
- /**
- * Render the template and return HTML
- *
- * @return {String}
- */
- mw.cards.Card.prototype.getHtml = function () {
- return this.template.render( this.article );
- };
-} )();
diff --git a/resources/view/CardList.js b/resources/view/CardList.js
deleted file mode 100644
index d509694..0000000
--- a/resources/view/CardList.js
+++ /dev/null
@@ -1,36 +0,0 @@
-( function () {
- 'use strict';
-
- var Card = mw.cards.Card;
-
- /**
- * View that handles multiple {@link mw.cards.Card cards}
- *
- * @class
- * @param {mw.cards.Card[]} cards
- */
- mw.cards.CardList = function ( cards ) {
- this.cards = cards || [];
- };
-
- OO.initClass( mw.cards.CardList );
-
- mw.cards.CardList.prototype.template = mw.template.get(
'ext.cards.view', 'cards.hogan' );
-
- /**
- * Render the cards and return HTML
- *
- * @return {String}
- */
- mw.cards.CardList.prototype.getHtml = function () {
- return this.template.render(
- {
- cards: this.cards
- },
- {
- card: Card.prototype.template
- }
- );
-
- };
-} )();
diff --git a/resources/view/cards.hogan b/resources/view/cards.hogan
deleted file mode 100644
index 149ec4c..0000000
--- a/resources/view/cards.hogan
+++ /dev/null
@@ -1,9 +0,0 @@
-{{# cards.length }}
- <ul class="ext-cards-page-list">
- {{# cards }}
- {{# article }}
- {{> card }}
- {{/ article }}
- {{/ cards }}
- </ul>
-{{/ cards.length }}
diff --git a/resources/views/Card.js b/resources/views/Card.js
new file mode 100644
index 0000000..d690c6f
--- /dev/null
+++ b/resources/views/Card.js
@@ -0,0 +1,86 @@
+( function ( $ ) {
+ 'use strict';
+
+ var CardView;
+
+ /**
+ * View that represents a card model
+ *
+ * @class
+ * @param {mw.cards.models.Card} model
+ */
+ CardView = function ( model ) {
+ this.model = model;
+ // listen to model changes and re-render the view
+ this.model.on( 'change', this._reRender.bind( this ) );
+
+ this.$el = $( this._template.render( this.model.data ) );
+ this._bindEvents();
+ };
+ OO.initClass( CardView );
+
+ /**
+ * Template
+ *
+ * @private
+ */
+ CardView.prototype._template = mw.template.get( 'ext.cards.views',
'card.hogan' );
+
+ /**
+ * Events and their handlers
+ * Keys are events and selectors separated by a space. Values are event
handlers.
+ * <code>
+ * 'click .some-selector': 'someHandler'
+ * </code>
+ *
+ * @type {Object}
+ * @private
+ */
+ CardView.prototype._events = {
+ };
+
+ /**
+ * Remove event handlers from this.$el using this._events
+ *
+ * @private
+ */
+ CardView.prototype._unBindEvents = function () {
+ var self = this;
+
+ $.each( this._events, function ( eventAndSelector ) {
+ eventAndSelector = eventAndSelector.split( ' ' );
+ self.$el.find( eventAndSelector[ 1 ] ).off(
eventAndSelector[ 0 ] );
+ } );
+ };
+
+ /**
+ * Bind event handlers to this.$el using this._events
+ *
+ * @private
+ */
+ CardView.prototype._bindEvents = function () {
+ var self = this;
+
+ $.each( this._events, function ( eventAndSelector, handler ) {
+ eventAndSelector = eventAndSelector.split( ' ' );
+ self.$el.find( eventAndSelector[ 1 ] ).on(
eventAndSelector[ 0 ], function () {
+ self[ handler ]();
+ } );
+ } );
+ };
+
+ /**
+ * Replace the html of this.$el with a newly rendered html using the
model data.
+ * Events attached to the previous element are removed and those events
are bound
+ * to the new object.
+ *
+ * @private
+ */
+ CardView.prototype._reRender = function () {
+ this._unBindEvents();
+ this.$el.replaceWith( this._template.render( this.model.data )
);
+ this._bindEvents();
+ };
+
+ mw.cards.views.Card = CardView;
+} )( jQuery );
diff --git a/resources/views/CardList.js b/resources/views/CardList.js
new file mode 100644
index 0000000..e3e3310
--- /dev/null
+++ b/resources/views/CardList.js
@@ -0,0 +1,33 @@
+( function ( $ ) {
+ 'use strict';
+
+ var CardListView;
+
+ /**
+ * View that handles multiple {@link mw.cards.views.Card cards}
+ *
+ * @class
+ * @param {mw.cards.views.Card[]} cardViews
+ */
+ CardListView = function ( cardViews ) {
+ var self = this;
+
+ this.cardViews = cardViews || [];
+ this.$el = $( this._template.render() );
+
+ // We don't want to use template partials because we want to
+ // preserve event handlers of each card view.
+ $.each( this.cardViews, function ( i, cardView ) {
+ self.$el.append( cardView.$el );
+ } );
+ };
+ OO.initClass( CardListView );
+
+ /**
+ * Template
+ * @private
+ */
+ CardListView.prototype._template = mw.template.get( 'ext.cards.views',
'cards.hogan' );
+
+ mw.cards.views.CardList = CardListView;
+} )( jQuery );
diff --git a/resources/view/card.hogan b/resources/views/card.hogan
similarity index 100%
rename from resources/view/card.hogan
rename to resources/views/card.hogan
diff --git a/resources/views/cards.hogan b/resources/views/cards.hogan
new file mode 100644
index 0000000..ab32b5f
--- /dev/null
+++ b/resources/views/cards.hogan
@@ -0,0 +1,2 @@
+<ul class="ext-cards-page-list">
+</ul>
diff --git a/resources/view/noimage.png b/resources/views/noimage.png
similarity index 100%
rename from resources/view/noimage.png
rename to resources/views/noimage.png
Binary files differ
diff --git a/resources/view/noimage.svg b/resources/views/noimage.svg
similarity index 100%
rename from resources/view/noimage.svg
rename to resources/views/noimage.svg
diff --git a/resources/view/styles.less b/resources/views/styles.less
similarity index 100%
rename from resources/view/styles.less
rename to resources/views/styles.less
--
To view, visit https://gerrit.wikimedia.org/r/249991
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie110c1d96000fa9ec1ccb3029e84b7ad035d528f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Cards
Gerrit-Branch: master
Gerrit-Owner: Bmansurov <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits