/static/js/models/thumbnails.js
JavaScript | 233 lines | 120 code | 32 blank | 81 comment | 6 complexity | 4ad89c27718ac6ebac068e98a52de6fa MD5 | raw file
Possible License(s): Apache-2.0
- define([
- 'config',
- 'underscore',
- 'backbone',
- 'models/livecollection',
- 'lib/template',
- 'lib/objecttools',
- 'lib/errors',
- 'lib/lazily',
- 'logger'
- ], function (
- config,
- util,
- Backbone,
- LiveCollection,
- template,
- objectTools,
- errors,
- lazily,
- logger
- ) {
-
- // Cache underscore templates for URL endpoints.
- var makeStatusUrl = template(config.thumbnailerStatus);
-
- var ThumbnailModel = Backbone.Model.extend({
- idAttribute: 'thumbnail_key'
- });
- // A simple reference object containing width/height objects for
- // thumbnails at various keyword sizes.
- var thumbnailSizes = {
- '1': {
- width: 150,
- height: 100
- },
- '2': {
- width: 300,
- height: 200
- }
- };
- // Get the default thumbnail size based on device pixel ratio.
- var thumbnailSize = thumbnailSizes[config.devicePixelRatio];
-
- var translateResponseKeys = util.bind(objectTools.translate, null, {
- // Convert key to id, so we can use all of the nice
- // collection/model getters.
- 'key': 'thumbnail_key',
- 'status': 'thumbnail_status'
- });
-
- var ThumbnailJobCollection = LiveCollection.extend({
- model: ThumbnailModel,
-
- initialize: function (attributes, options) {
- options = options || {};
- this.jobId = options.jobId;
- logger.log('New Thumbnail Job Collection', this.jobId);
- this.bind('success', this.onSuccess, this);
- },
- url: lazily(function () {
- if (!this.jobId) throw new errors.IdMissingError( "required attribute jobId missing from ThumbnailJobCollection instance" );
- return makeStatusUrl({ jobId: this.jobId });
- }),
-
- pollUntilFinishedProcessing: function () {
- this.stream({
- interval: 3000,
- tries: 10,
- diff: true
- });
- },
-
- onSuccess: function (resp, options) {
- var thumbnails = util.map(resp.sites, translateResponseKeys),
- collection = this;
- logger.log(
- 'Polled Thumbnail Job API',
- '(' + collection.length + ' of ' + thumbnails.length + ' ready)',
- collection.url()
- );
- // When the job id is generated by the caller (social code), there is
- // the possibility that the returned thumbnail list is empty the first
- // few polls.
- if (thumbnails.length === 0) {
- return;
- }
-
- // Get all of the statuses from thumbnails
- var statuses = util.pluck(thumbnails, 'thumbnail_status');
- // There are 3 thumbnail statuses:
- //
- // 1. processing
- // 2. error
- // 3. ready
- //
- // Filter out everything that is not processing, and see if there
- // is anything left over. If all thumbnails are processed,
- // stop polling.
- if (util.indexOf(statuses, 'processing') === -1) {
- collection.unstream();
- }
- },
- // Defining a custom parse implementation that massages data
- // from our JSON API into a Backbone.Model-compatible format.
- //
- // Sample return data:
- //
- // {
- // "job": "...",
- // "sites": [
- // {
- // "key": "...",
- // "status": "processing",
- // "url": "http://instapaper.com/"
- // }
- // ...
- // ],
- // "success": true
- // }
- //
- // ...which is turned into:
- //
- // [
- // {
- // "thumbnail_key": "...",
- // "thumbnail_status": "processing",
- // // Created from template + ID during parse:
- // "thumbnail_url": "http://example.com/example.png"
- // }
- // ...
- // ]
- parse: function (resp) {
- // Normalize response keys
- var thumbnails = util.map(resp.sites, translateResponseKeys);
- // Make sure JobID is correct.
- // If no job is found in response, log the response for reference.
- //
- // This case is known to happen when a stack is requested, and it comes
- // back with no results. The stack API will hand us a thumbnail job ID
- // for that stack, but when the ID is handed to the thumbnailer, it
- // will return:
- //
- // {
- // "reason": "no such job",
- // "success": false
- // }
- if (!thumbnails) {
- logger.log('No thumbnails were found in job.', resp);
- }
- // Filter out thumbnails that aren't ready.
- thumbnails = util.filter(thumbnails, function (thumbnail) {
- return thumbnail.thumbnail_status === 'ready';
- });
- // Create a template function for the `image_url` description.
- var makeThumbnailUrl = template(resp.image_url);
-
- // Add `thumbnail_url` property.
- thumbnails = util.map(thumbnails, function (thumbnail) {
- thumbnail.thumbnail_url = makeThumbnailUrl(util.extend({
- key: thumbnail.thumbnail_key
- }, thumbnailSize));
- return thumbnail;
- });
- // Return the full list of ready thumbnails. Since this method is a
- // child of LiveCollection, `add` will by default de-dupe thumbnails
- // with the same ID.
- return thumbnails;
- }
- });
-
- // A parse implementation that gives you everything you need to spawn
- // ThumbnailJobCollections. Call from within other parse methods:
- //
- // resp = thumbnails.parse.call(this, resp);
- var parse = function (resp) {
- this.trigger('parse', this, resp);
- this.jobId = resp.thumbnails_job;
- return resp;
- };
- // Manages thumbnail jobs by ID. Makes sure there is never more than one
- // job per ID.
- var jobManager = {
- jobs: [],
- // A factory for job collections. If your job doesn't exist yet, it
- // will create one. Bind to the collection's `reset` event to get updates.
- // TODO: we should be calling `add` event on fetch.
- job: function (key) {
- var job = this.jobs[key];
- // Hit the callback if we already have a stored job.
- if (!job) {
- // Create a new thumbnail job collection. Set the key of the
- // thumbnail job (which ThumbnailJobCollection turns into a URL).
- var collection = new ThumbnailJobCollection([], {
- jobId: key
- });
- collection.pollUntilFinishedProcessing();
- // Memoize. Make sure this job is not duplicated.
- job = this.jobs[key] = collection;
- }
- return job;
- }
- };
- // Create a bound version of the job manager.
- var getJobFromManager = util.bind(jobManager.job, jobManager);
-
- return {
- ThumbnailModel: ThumbnailModel,
- JobCollection: ThumbnailJobCollection,
- getJobFromManager: getJobFromManager,
- parse: parse
- };
- });