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

Reply via email to