function()

in public/src/js/models/collections/article.js [30:489]


    function (
        vars,
        ko,
        _,
        $,
        metaFields,
        alert,
        asObservableProps,
        deepGet,
        humanTime,
        isGuardianUrl,
        isPreviewUrl,
        logger,
        mediator,
        openGraph,
        populateObservables,
        serializeArticleMeta,
        snap,
        urlAbsPath,
        visitedArticleStorage,
        copiedArticle,
        authedAjax,
        contentApi,
        Editor,
        images,
        persistence,
        Group
    ) {
        alert = alert.default;
        deepGet = deepGet.default;
        isGuardianUrl = isGuardianUrl.default;
        isPreviewUrl = isPreviewUrl.default;
        urlAbsPath = urlAbsPath.default;
        asObservableProps = asObservableProps.default;
        serializeArticleMeta = serializeArticleMeta.default;
        populateObservables = populateObservables.default;
        mediator = mediator.default;
        humanTime = humanTime.default;
        visitedArticleStorage = visitedArticleStorage.default;
        copiedArticle = copiedArticle.default;
        logger = logger.default;
        Group = Group.default;
        metaFields = metaFields.default;
        openGraph = openGraph.default;
        persistence = persistence.default;

        var createEditor = Editor.default.create;

        var capiProps = [
                'webUrl',
                'webPublicationDate',
                'sectionName'],

            capiFields = [
                'headline',
                'trailText',
                'byline',
                'isLive',
                'firstPublicationDate',
                'scheduledPublicationDate',
                'thumbnail',
                'secureThumbnail'];

        function Article(opts, withCapiData) {
            var self = this;

            opts = opts || {};

            this.dropTarget = true;
            this.id = ko.observable(opts.id);

            this.group = opts.group;

            this.front = opts.group ? opts.group.front : null;

            this.props = asObservableProps(capiProps);
            this.props.webPublicationDate.extend({ notify: 'always' });

            this.fields = asObservableProps(capiFields);

            this.meta = asObservableProps(_.pluck(metaFields, 'key'));

            populateObservables(this.meta, opts.meta);

            this.metaDefaults = {};

            this.collectionMetaDefaults = deepGet(opts, '.group.parent.itemDefaults');

            this.uneditable = opts.uneditable;

            this.state = asObservableProps([
                'enableContentOverrides',
                'underDrag',
                'underControlDrag',
                'isOpen',
                'isLiveBlog',
                'isLoaded',
                'isEmpty',
                'visited',
                'tone',
                'primaryTag',
                'sectionName',
                'hasMainVideo',
                'imageCutoutSrcFromCapi',
                'ophanUrl',
                'sparkUrl',
                'premium']);

            this.state.enableContentOverrides(this.meta.snapType() !== 'latest');
            this.state.visited(opts.visited);

            this.frontPublicationDate = opts.frontPublicationDate;
            this.publishedBy = opts.publishedBy;
            this.frontPublicationTime = ko.observable();
            this.scheduledPublicationTime = ko.observable();

            this.editors = ko.observableArray();

            this.editorsDisplay = ko.observableArray();

            this.headline = ko.pureComputed(function () {
                var meta = this.meta, fields = this.fields;
                if (this.state.enableContentOverrides()) {
                    return meta.headline() || fields.headline() || (meta.snapType() ? 'No headline!' : 'Loading...');
                } else {
                    return '{ ' + meta.customKicker() + ' }';
                }
            }, this);

            this.headlineLength = ko.pureComputed(function() {
                return (this.meta.headline() || this.fields.headline() || '').length;
            }, this);

            this.headlineLengthAlert = ko.pureComputed(function() {
                return (this.meta.headline() || this.fields.headline() || '').length > vars.CONST.restrictedHeadlineLength;
            }, this);


            this.webPublicationTime = ko.pureComputed(function(){
                return humanTime(this.props.webPublicationDate());
            }, this);

            this.viewUrl = ko.pureComputed(function() {
                var url;
                if (this.fields.isLive() === 'false') {
                    url = vars.CONST.previewBase + '/' + urlAbsPath(this.props.webUrl());
                } else {
                    url = this.meta.href() || this.props.webUrl();

                    if (url && !/^https?:\/\//.test(url)) {
                        url = 'http://' + vars.CONST.mainDomain + url;
                    }
                }

                return url;
            }, this);

            // Populate supporting
            if (this.group && this.group.parentType !== 'Article') {
                this.meta.supporting = new Group({
                    parent: self,
                    parentType: 'Article',
                    omitItem: self.save.bind(self),
                    front: self.front
                });

                this.meta.supporting.items(_.map((opts.meta || {}).supporting, function (item) {
                    return new Article(_.extend(item, {
                        group: self.meta.supporting
                    }));
                }));

                contentApi.decorateItems(this.meta.supporting.items());
            }

            if (withCapiData) {
                this.addCapiData(opts);
            } else {
                this.updateEditorsDisplay();
            }

            this.thumbImage = ko.pureComputed(images.thumbnail, this);
        }

        Article.prototype.copy = function () {
            copiedArticle.set(this);
        };

        Article.prototype.copyToClipboard = function () {
            mediator.emit('copy:to:clipboard', this.get());
        };

        Article.prototype.setVisitedToTrue = function () {
            visitedArticleStorage.addArticleToStorage(this.id());
            mediator.emit('set:article:to:visited', this.id());
            return true;
        };

        Article.prototype.paste = function () {
            var sourceItem = copiedArticle.get(true);

            if (!sourceItem || sourceItem.id === this.id()) { return; }

            mediator.emit('drop', {
                sourceItem: sourceItem.article.get(),
                sourceGroup: sourceItem.group
            }, this, this.group);
        };

        Article.prototype.metaDisplayer = function (opts, index, all) {
            var self = this,
                display,
                label;

            if (opts.type === 'boolean') {
                display = opts.editable;
                display = display && (this.meta[opts.key] || function() {})();
                display = display && (opts.omitIfNo ? _.some(all, function(editor) { return editor.key === opts.omitIfNo && self.meta[editor.key](); }) : true);

                label = _.chain([
                    opts.label,
                    _.result(this.state, opts.labelState),
                    _.result(this.meta,  opts.labelMeta)
                ])
                .compact()
                .value()
                .join(': ');

                return display ? label : false;
            } else {
                return false;
            }
        };

        Article.prototype.addCapiData = function(opts) {
            var missingProps;

            populateObservables(this.props,  opts);
            populateObservables(this.fields, opts.fields);

            this.setRelativeTimes();

            missingProps = [
                'webUrl',
                'fields',
                'fields.headline'
            ].filter(function(prop) {return !deepGet(opts, prop); });

            if (missingProps.length) {
                mediator.emit('capi:error', 'ContentApi is returning invalid data. Fronts may not update.');
                logger.error('ContentApi missing: "' + missingProps.join('", "') + '" for ' + this.id());
            } else {
                this.state.isLoaded(true);
                this.state.sectionName(this.props.sectionName());
                this.state.primaryTag(getPrimaryTag(opts));
                this.state.imageCutoutSrcFromCapi(getContributorImage(opts));
                this.state.hasMainVideo(getMainMediaType(opts) === 'video');
                this.state.tone(opts.frontsMeta && opts.frontsMeta.tone);
                this.state.ophanUrl(vars.CONST.ophanBase + '?path=/' + urlAbsPath(opts.webUrl));
                this.state.premium(isPremium(opts));
                if (deepGet(opts, '.fields.liveBloggingNow') === 'true') {
                    this.state.isLiveBlog(true);
                }

                this.metaDefaults = _.extend(deepGet(opts, '.frontsMeta.defaults') || {}, this.collectionMetaDefaults);

                populateObservables(this.meta, this.metaDefaults);

                this.updateEditorsDisplay();
            }
        };

        Article.prototype.updateEditorsDisplay = function() {
            if (!this.uneditable) {
                this.editorsDisplay(metaFields.map(this.metaDisplayer, this).filter(Boolean));
            }
        };

        Article.prototype.setRelativeTimes = function() {
            this.frontPublicationTime(humanTime(this.frontPublicationDate));
            this.scheduledPublicationTime(humanTime(this.fields.scheduledPublicationDate()));
        };

        Article.prototype.get = function() {
            var asObject = {
                id: this.id()
            };
            var meta = serializeArticleMeta(this);
            if (meta) {
                asObject.meta = meta;
            }
            return asObject;
        };

        Article.prototype.normalizeDropTarget = function() {
            return {
                isAfter: false,
                target: this
            };
        };

        Article.prototype.save = function() {
            return persistence.article.save(this);
        };

        Article.prototype.convertToSnap = function() {
            var id = this.id();
            var href;

            if (isGuardianUrl(id) || isPreviewUrl(id)) {
                href = '/' + urlAbsPath(id);
            } else {
                href = id;
            }

            this.meta.href(href);
            this.id(snap.generateId());
            this.updateEditorsDisplay();
        };

        Article.prototype.convertToLinkSnap = function() {
            if (!this.meta.headline()) {
                this.decorateFromOpenGraph();
            }

            this.meta.snapType('link');

            this.convertToSnap();
        };

        Article.prototype.convertToLatestSnap = function(kicker) {
            this.meta.snapType('latest');
            this.meta.snapUri(urlAbsPath(this.id()));

            this.meta.showKickerCustom(true);
            this.meta.customKicker(kicker);

            this.meta.headline(undefined);
            this.meta.trailText(undefined);
            this.meta.byline(undefined);

            this.state.enableContentOverrides(false);

            this.convertToSnap();
        };

        Article.prototype.decorateFromOpenGraph = function() {
            var thisArticle = this;

            this.meta.headline('Fetching headline...');

            return openGraph(this.id())
            .then(function (data) {
                thisArticle.meta.headline(data.title);
                thisArticle.meta.trailText(data.description);

                if (data.siteName) {
                    thisArticle.meta.byline(data.siteName);
                    thisArticle.meta.showByline(true);
                }
            })
            .catch(function () {
                thisArticle.meta.headline('Invalid page');
            })
            .then(function () {
                thisArticle.updateEditorsDisplay();
            });
        };

        Article.prototype.open = function(article, evt) {
            if (this.uneditable) { return; }

            if (this.meta.supporting) { this.meta.supporting.items().forEach(function(sublink) { sublink.close(); }); }

            if (!this.state.isOpen()) {
                if (this.editors().length === 0) {
                    this.editors(metaFields.map(function (field) {
                        return createEditor(field, this, metaFields);
                    }, this).filter(Boolean));
                }
                this.state.isOpen(true);
                mediator.emit(
                    'ui:open',
                    _.chain(this.editors())
                     .filter(function(editor) { return editor.type === 'text' && editor.displayEditor(); })
                     .map(function(editor) { return editor.meta; })
                     .first()
                     .value(),
                    this,
                    this.front
                );
            } else {
                mediator.emit('ui:open', null, null, this.front);
            }

            if ($(evt.target).hasClass('allow-default-click')) {
                return true;
            }
        };

        Article.prototype.close = function() {
            if (this.state.isOpen()) {
                this.state.isOpen(false);
                this.updateEditorsDisplay();
            }
            mediator.emit('ui:close', {
                targetGroup: this.group
            });
        };

        Article.prototype.closeAndSave = function() {
            this.close();
            this.save();
            return false;
        };

        Article.prototype.closeWithoutSaving = function() {
            this.close();
            if ( this.group && this.group.parentType === 'Collection' ) {
                this.group.parent.replaceArticle(this.id());
            }
            return false;
        };

        Article.prototype.omitItem = function () {
            this.group.omitItem(this);
        };

        Article.prototype.dispose = function () {
            if (this.meta.supporting) {
                this.meta.supporting.dispose();
            }
            this.editors().forEach(function (editor) {
                editor.dispose();
            });
            this.editors.removeAll();
        };

        function getMainMediaType(contentApiArticle) {
            return _.chain(contentApiArticle.elements).where({relation: 'main'}).pluck('type').first().value();
        }

        function getPrimaryTag(contentApiArticle) {
            return _.chain(contentApiArticle.tags).pluck('webTitle').first().value();
        }

        function getContributorImage(contentApiArticle) {
            var contributors = _.chain(contentApiArticle.tags).where({type: 'contributor'});

            return contributors.value().length === 1 ? contributors.pluck('bylineLargeImageUrl').first().value() : undefined;
        }

        function isPremium(contentApiArticle) {
            return contentApiArticle.fields.membershipAccess === 'members-only' ||
                contentApiArticle.fields.membershipAccess === 'paid-members-only' ||
                !!_.find(contentApiArticle.tags, {id: 'news/series/looking-back'});
        }

        return Article;
    });