Source: views/portals/PortalSectionView.js

define(["jquery",
    "underscore",
    "backbone",
    'models/portals/PortalSectionModel',
    "views/MarkdownView",
    "views/TOCView",
    "text!templates/portals/portalSection.html"],
    function($, _, Backbone, PortalSectionModel, MarkdownView, TOCView, Template){

    /**
     * @class PortalSectionView
     * @classdesc The PortalSectionView is a generic view to render
     * portal sections, with a default rendering of a
     * MarkdownView
     * @module views/PortalSectionView
     * @name PortalSectionView
     * @extends Backbone.View
     * @constructor
     */
     var PortalSectionView = Backbone.View.extend(
       /** @lends PortalSectionView.prototype */{

       /**
       * The type of View this is
       * @type {string}
       * @readonly
       */
        type: "PortalSection",

        /**
        * The display name for this Section
        * @type {string}
        */
        sectionName: "",

        /**
        * The unique label for this Section. It most likely matches the label on the model, but
        * may include a number after if more than one section has the same name.
        * @type {string}
        */
        uniqueSectionLabel: "",

        /**
        * The HTML tag name for this view's element
        * @type {string}
        */
        tagName: "div",

        /**
        * The HTML classes to use for this view's element
        * @type {string}
        */
        className: "tab-pane portal-section-view",

        /**
        * Specifies if this section is active or not
        * @type {boolean}
        */
        active: false,

        /**
        * The PortalSectionModel that is being edited
        * @type {PortalSection}
        */
        model: undefined,

        /**
        * @type {UnderscoreTemplate}
        */
        template: _.template(Template),

        /**
        * @param {Object} options - A literal object with options to pass to the view
        * @property {PortalSection} options.model - The PortalSection rendered in this view
        * @property {string} options.sectionName - The name of the portal section
        */
        initialize: function(options){

          // Get all the options and apply them to this view
          if( typeof options == "object" ) {
              var optionKeys = Object.keys(options);
              _.each(optionKeys, function(key, i) {
                  this[key] = options[key];
              }, this);
          }

        },

        /**
         Renders the view
        */
        render: function() {

          //Add the active class to the element
          if( this.active ){
            this.$el.addClass("active");
          }

          //Add an id to this element so links work
          this.$el.attr("id", this.getName({ linkFriendly: true }));

          this.$el.html(this.template({
            imageURL: this.model.get("image")? this.model.get("image").get("imageURL") : "",
            title: this.model.get("title"),
            introduction: this.model.get("introduction")
          }));

          this.$el.data("view", this);

          //If there is Markdown, render it
          if( this.model.get("content").get("markdown") ){
            //Create a MarkdownView
            var sectionMarkdownView = new MarkdownView({
              markdown: this.model.get("content").get("markdown"),
              citations: this.model.get("literatureCited")
            });

            //Listen to the markdown view and when it is rendered, format the rendered markdown
            this.listenTo(sectionMarkdownView, "mdRendered", this.postMarkdownRender);

            //Render the view
            sectionMarkdownView.render();

            //Add the markdown view element to this view
            this.$(".portal-section-content").html(sectionMarkdownView.el);

            this.markdownView = sectionMarkdownView;

            //Set TOC to render after the Markdown section, so it
            // can get the rendered header tags
            this.listenToOnce(sectionMarkdownView, "mdRendered", this.renderTOC);
          }

        },

        /**
        * Renders a table of contents (a TOCView) that links to different sections of the MarkdownView
        */
        renderTOC: function(){

          //Create a table of contents view
          var tocView = new TOCView({
            contentEl: this.markdownView.el,
            className: "toc toc-view scrollspy-TOC"
          });

          this.tocView = tocView;

          tocView.render();

          //If more than one link was created in the TOCView, add it to this view
          if( tocView.$el.find("li").length > 1){
            this.$(".portal-section-content").prepend(tocView.el);

            //Make a two-column layout
            tocView.$el.addClass("span3");
            this.markdownView.$el.addClass("span9");
          }

        },

        /**
        * This funciton is called after this view has fully rendered and is
        * visible on the webpage
        */
        postRender: function(){

          _.each(this.subviews, function(subview){
              if(subview.postRender){
                subview.postRender();
              }
          });

          // When TOC is added...
          // Affix the TOC to the top of the window when scrolling past it &
          // add scrollSpy
          if( !this.tocView && this.markdownView ){
            this.listenToOnce(this.markdownView, "mdRendered", function(){
              this.tocView.$el.affix({
                offset: this.tocView.$el.offset().top
              });
              this.tocView.addScrollspy(".portal-section-view.active .scrollspy-TOC");
            });
          }
          else if( this.tocView ){
            this.tocView.$el.affix({
              offset: this.tocView.$el.offset().top
            });
            this.tocView.addScrollspy(".portal-section-view.active .scrollspy-TOC");
          }

        },

        /**
        * When the portal section markdown is rendered in a MarkdownView, format the
        * resulting HTML as needed for this view
        */
        postMarkdownRender: function(){

          this.$(".markdown img").addClass("thumbnail").after("<div class='clear'></div>");

          //If the window location has a hash, scroll to it
          if( window.location.hash && this.$(window.location.hash).length ){

            var view = this;

            //Wait 0.5 seconds to allow images time to load before we scroll down the page
            setTimeout(function(){
              //Scroll to the element specified in the hash
              MetacatUI.appView.scrollTo( view.$(window.location.hash) );
            }, 500);
          }

        },

        /**
        * Gets the name of this section and returns it
        * @param {Object} [options] - Optional options for the name that is returned
        * @property {Boolean} options.linkFriendly - If true, the name will be stripped of special characters
        * @return {string} The name for this section
        */
        getName: function(options){

          var name = "";

          //If a section name is set on the view, use that
          if( this.sectionName ){
            name = this.sectionName;
          }
          //If the model is a PortalSectionModel, use the label from the model
          else if( PortalSectionModel.prototype.isPrototypeOf(this.model) ){
            name = this.model.get("label");
          }
          else{
            name = "Untitled";
          }

          if( typeof options == "object" ){
            if( options.linkFriendly ){
              name = name.replace(/[^a-zA-Z0-9 ]/g, "").replace(/ /g, "-");
            }
          }

          return name;

        }
     });

     return PortalSectionView;
});