<commit_before>/*
* @Author: Austin Liu
* @Date:   2015-06-01 17:41:31
* @Last Modified by:   kuychaco
* @Last Modified time: 2015-06-02 19:08:59

*/

// 'use strict';

var sampleGraph = JSON.stringify({
  entry: 2,
  1: {
    "id": 1, // PRIMARY KEY
    "type": "cluster",
    "parent_cluster": null, // foreign key ID from NODES table
    "cluster_id": {
      "id": 1,  // PRIMARY KEY
      "abbrev": "ROOT",  // must be less than 32 chars
      "name": "Project Root",
      "description": "Cluster of entire project",
      "endpoints": [2, 3],  // these foreign key IDs for entries in NODES table
      "creator": 1445825  // foreign key ID for entry in USERS table
    }, // foreign key ID from CLUSTERS table
    "issue_id": null, // foreign key ID from ISSUES table
    "upstream_nodes": [], // foreign key ID from NODES table
    "downstream_nodes": [] // foreign key ID from NODES table
  },
  2: {
    "id": 2,// PRIMARY KEY
    "type": "entry",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": null, // foreign key ID from CLUSTERS table
    "issue_id": null, // foreign key ID from ISSUES table
    "upstream_nodes": null, // foreign key ID from NODES table
    "downstream_nodes": [4,6] // foreign key ID from NODES table
  },
  3: {
    "id": 3,// PRIMARY KEY
    "type": "exit",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": null, // foreign key ID from CLUSTERS table
    "issue_id": null, // foreign key ID from ISSUES table
    "upstream_nodes": [5,7], // foreign key ID from NODES table
    "downstream_nodes": [] // foreign key ID from NODES table
  },
  4: {
    "id": 4,// PRIMARY KEY
    "type": "issue",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": null, // foreign key ID from CLUSTERS table
    "issue_id": {
      "id": 82639324,
      "url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/4",
      "labels_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/4/labels{/name}",
      "comments_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/4/comments",
      "events_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/4/events",
      "html_url": "https://github.com/relentlessbreakfast/sampleGraph/issues/4",
      "number": 4,
      "title": "Add O-auth",
      "user": 1445825,
      "labels": [1],
      "state": "open",
      "locked": false,
      "assignee": 442978,
      "comments": 0,
      "created_at": "2015-05-30T00:16:35Z",
      "updated_at": "2015-05-30T00:44:37Z",
      "closed_at": null,
      "body": "Type:\ * issue\ \ Upstream:\ * entry\ \ Downstream:\ * Cluster-Repo Selection Screen\ * Make sample graph data"
    }, // foreign key ID from ISSUES table
    "upstream_nodes": [2], // foreign key ID from NODES table
    "downstream_nodes": [5,7] // foreign key ID from NODES table
  },
  5: {
    "id": 5,// PRIMARY KEY
    "type": "cluster",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": {
      "id": 3,  // PRIMARY KEY
      "abbrev": "Repo selection",  // must be less than 32 chars
      "name": "Cluster-Repo Selection Screen",
      "description": "Cluster of repo selection related tasks",
      "endpoints": [13, 14],  // these foreign key IDs for entries in NODES table
      "creator": 1445825  // foreign key ID for entry in USERS table
    }, // foreign key ID from CLUSTERS table
    "issue_id": null, // foreign key ID from ISSUES table
    "upstream_nodes": [4,6], // foreign key ID from NODES table
    "downstream_nodes": [3] // foreign key ID from NODES table
  },
  6: {
    "id": 6,// PRIMARY KEY
    "type": "cluster",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": {
      "id": 2,  // PRIMARY KEY
      "abbrev": "DB",  // must be less than 32 chars
      "name": "Cluster-Database Schema",
      "description": "Cluster of database schema related tasks",
      "endpoints": [11, 12],  // these foreign key IDs for entries in NODES table
      "creator": 1445825  // foreign key ID for entry in USERS table
    }, // foreign key ID from CLUSTERS table
    "issue_id": null, // foreign key ID from ISSUES table
    "upstream_nodes": [2], // foreign key ID from NODES table
    "downstream_nodes": [5,7] // foreign key ID from NODES table
  },
  7: {
    "id": 7,// PRIMARY KEY
    "type": "issue",
    "parent_cluster": 1, // foreign key ID from NODES table
    "cluster_id": null, // foreign key ID from CLUSTERS table
    "issue_id": {
      "id": 82639733, // PRIMARY KEY
      "url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/7",
      "labels_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/7/labels{/name}",
      "comments_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/7/comments",
      "events_url": "https://api.github.com/repos/relentlessbreakfast/sampleGraph/issues/7/events",
      "html_url": "https://github.com/relentlessbreakfast/sampleGraph/issues/7",
      "number": 7,
      "title": "Make sample graph data",
      "user": 1445825,
      "labels": [6],
      "state": "open",
      "locked": false,
      "assignee": 1445825,
      "comments": 2,
      "created_at": "2015-05-30T00:18:26Z",
      "updated_at": "2015-05-30T00:43:54Z",
      "closed_at": null,
      "body": "type:\ * Issue\ \ Upstream:\ * entry\ \ Downstream:\ * Cluster-Repo Selection Screen\ * Make sample graph data"
    }, // foreign key ID from ISSUES table
    "upstream_nodes": [4,6], // foreign key ID from NODES table
    "downstream_nodes": [3] // foreign key ID from NODES table
  }
});

/* * * * * GRAPH CLASS * * * * */

// input is parsed graph JSON
var WrappedGraph = function(graphObj) {
  this.graph = graphObj;
};

/* * * * * GRAPH OPERATIONS * * * * */

/* * * * * Add/Remove Node * * * * */

WrappedGraph.prototype.deleteNode = function(nodeId) {
  var wrappedGraph = this;
  // store upstream and downstream arrays
  var upstream = this.graph[nodeId].upstream_nodes.slice() || [];
  var downstream = this.graph[nodeId].downstream_nodes.slice() || [];
  // break links to nodeId
  upstream.forEach(function(upNodeId) {
    wrappedGraph.unlinkNodes(upNodeId, nodeId);
  });
  downstream.forEach(function(downNodeId) {
    wrappedGraph.unlinkNodes(nodeId, downNodeId);
  });
  // remove node from graph object
  delete wrappedGraph.graph[nodeId];
  // create links between upstream and downstream nodes
  upstream.forEach(function(upNodeId) {
    downstream.forEach(function(downNodeId) {
      // linkNodes calls transitive reduction
      wrappedGraph.linkNodes(upNodeId, downNodeId);
    });
  });
};

/* * * * * Add/Remove link * * * * */

WrappedGraph.prototype.linkNodes = function(upNodeId, downNodeId) {
  // adds appropriate nodeIds to upstream and downstream arrays
  this.graph[upNodeId].downstream_nodes.push(downNodeId);
  this.graph[downNodeId].upstream_nodes.push(upNodeId);
  // do transitive reduction
  this.transitiveReduction(downNodeId, upNodeId);
};

WrappedGraph.prototype.unlinkNodes = function(upNodeId, downNodeId) {
  // remove downNodeId from upNodeId's downstream array
  this.graph[upNodeId].downstream_nodes.forEach(function(nodeId, i, arr) {
    if (nodeId === downNodeId) {
      arr.splice(i,1);
    }
  });
  // remove upNodeId from downNodeId's upstream array
  this.graph[downNodeId].upstream_nodes.forEach(function(nodeId, i, arr) {
    if (nodeId === upNodeId) {
      arr.splice(i,1);
    }
  });
};

/* * * * * Transitive Reduction * * * * *
Given a particular node and a new dependency (new upstream node)
*/

WrappedGraph.prototype.gatherUpstreamNodeRefs = function(nodeId) {
  // catalog object w/ all upstream nodeIds as keys
  var catalog = {};
  var wrappedGraph = this;
  // recurse up to entry node and add nodeIds to catalog
  var recurse = function (nodeId) {
    wrappedGraph.graph[nodeId].upstream_nodes && wrappedGraph.graph[nodeId].upstream_nodes.forEach(function(upNodeId) {
      catalog[upNodeId] = true;
      recurse(upNodeId);
    });
  };
  recurse(nodeId);

  return catalog;
};

WrappedGraph.prototype.purgeUplinksFromANode = function(nodeId, catalogObj) {
  var wrappedGraph = this;
  // iterate through nodeIds in upstream array for node with nodeId
  this.graph[nodeId].upstream_nodes && this.graph[nodeId].upstream_nodes.forEach(function(upNodeId) {
    // if uplinkNodeId is in catalogObj
    if (catalogObj.hasOwnProperty(upNodeId)) {
      wrappedGraph.unlinkNodes(upNodeId, nodeId);
    }
  });
};

WrappedGraph.prototype.purgeUplinksBelowANode = function(nodeId, catalogObj) {
  var wrappedGraph = this;
  // iterate through nodeIds in downstream array for node with nodeId
  this.graph[nodeId].downstream_nodes && this.graph[nodeId].downstream_nodes.forEach(function(downNodeId) {
    // purgeUplinksFromANode for each nodeId in upstream array of node
    wrappedGraph.purgeUplinksFromANode(downNodeId, catalogObj);
    // recursive call for each downstream nodeId
    wrappedGraph.purgeUplinksBelowANode(downNodeId, catalogObj);
  });
};

WrappedGraph.prototype.transitiveReduction = function(nodeId, newUpNodeId) {
  // gather all upstream nodeIds
  var catalog = this.gatherUpstreamNodeRefs(newUpNodeId);
  // remove all uplinks from node at nodeId
  this.purgeUplinksFromANode(nodeId, catalog);
  // add newUpNodeId to catalog
  catalog[newUpNodeId] = true;
  // remove nodeId from all downstream nodes
  this.purgeUplinksBelowANode(nodeId, catalog);
};

/* * * * * * * * * * * * * * * * * * */

/*
  matchers:
    - toBe, ===
    - toEqual, ==
    - toMatch, (regex)
    - toBeDefined, not undefined
    - toBeUndefined
    - toBeNull
    - toBeTruthy
    - toBeFalsey
    - toContain, searches array for value
    - toBeLessThan
    - toBeGreaterThan
    - toThrow, catch expected exceptions
 */

describe('Graph Class', function() {

  var graph;

  beforeEach(function() {
    graph = JSON.parse(sampleGraph);
  });

  describe('initial graph class', function() {
    var wrappedGraph;
    beforeEach(function() {
      wrappedGraph = new WrappedGraph(graph);
    });

    it('should be a function', function () {
      expect(typeof WrappedGraph).toBe('function');
    });

    it('should return an object with graph property', function () {
      expect(typeof wrappedGraph.graph).toBe('object');
    });

    it('should have connection between nodes 2 and 4', function () {
      expect(wrappedGraph.graph[2].downstream_nodes).toContain(4);
      expect(wrappedGraph.graph[4].upstream_nodes).toContain(2);
    });

    it('should not have connection between nodes 4 and 6', function () {
      expect(wrappedGraph.graph[6].downstream_nodes.indexOf(4)).toBe(-1);
      expect(wrappedGraph.graph[4].upstream_nodes.indexOf(6)).toBe(-1);
    });

    describe('transitive reduction', function() {

      beforeEach(function() {
        wrappedGraph.linkNodes(6,4);
      });

      it('should create link between nodes 4 and 6', function () {
        expect(wrappedGraph.graph[6].downstream_nodes).toContain(4);
        expect(wrappedGraph.graph[4].upstream_nodes).toContain(6);
      });

      it('should break links between nodes 2 and 4', function () {
        expect(wrappedGraph.graph[2].downstream_nodes.indexOf(4)).toBe(-1);
        expect(wrappedGraph.graph[4].upstream_nodes.indexOf(2)).toBe(-1);
      });

      it('should break links between nodes 6 and 7', function () {
        expect(wrappedGraph.graph[6].downstream_nodes.indexOf(7)).toBe(-1);
        expect(wrappedGraph.graph[7].upstream_nodes.indexOf(6)).toBe(-1);
      });

      it('should break links between nodes 6 and 5', function () {
        expect(wrappedGraph.graph[6].downstream_nodes.indexOf(5)).toBe(-1);
        expect(wrappedGraph.graph[5].upstream_nodes.indexOf(6)).toBe(-1);
      });

      describe('delete node', function() {

        beforeEach(function() {
          wrappedGraph.deleteNode(5);
        });

        it('should break link from 3/4 to 5', function () {
          expect(wrappedGraph.graph[4].downstream_nodes.indexOf(5)).toBe(-1);
          expect(wrappedGraph.graph[3].upstream_nodes.indexOf(5)).toBe(-1);
          expect(wrappedGraph.graph[5]).toBeUndefined();
        });

        it('transfer link from 3 to 4', function () {
          wrappedGraph.deleteNode(7);
          expect(wrappedGraph.graph[4].downstream_nodes).toContain(3);
          expect(wrappedGraph.graph[3].upstream_nodes).toContain(4);
        });
      });
    });

  });

});
<commit_msg>(test) Move class to service<commit_after>/*
* @Author: Austin Liu
* @Date:   2015-06-01 17:41:31
* @Last Modified by:   kuychaco
* @Last Modified time: 2015-06-03 16:03:14

*/

'use strict';

/*
  matchers:
    - toBe, ===
    - toEqual, ==
    - toMatch, (regex)
    - toBeDefined, not undefined
    - toBeUndefined
    - toBeNull
    - toBeTruthy
    - toBeFalsey
    - toContain, searches array for value
    - toBeLessThan
    - toBeGreaterThan
    - toThrow, catch expected exceptions
 */

var graphService;
var sampleGraph;
var graph;
beforeEach(module('cd-app.common'));
beforeEach(inject(function (_GraphService_, $q) {
 graphService = _GraphService_;
 sampleGraph = graphService.getGraph();
}));

describe('Graph Class', function() {

  describe('initial graph class', function() {
    var wrappedGraph;
    var WrappedGraph;
    beforeEach(function() {
      WrappedGraph = graphService.graphClass;
      console.log('----',sampleGraph);
      wrappedGraph = new WrappedGraph(sampleGraph);
      console.log('in before each',wrappedGraph);
    });

    it('should be a function', function () {
      expect(typeof WrappedGraph).toBe('function');
    });

    it('should return an object with graph property', function () {
      expect(typeof wrappedGraph.graph).toBe('object');
    });

    it('should have connection between nodes 2 and 4', function () {
      expect(wrappedGraph.graph[2].downstream_nodes).toContain(4);
      expect(wrappedGraph.graph[4].upstream_nodes).toContain(2);
    });

    it('should not have connection between nodes 4 and 6', function () {
      expect(wrappedGraph.graph[6].downstream_nodes.indexOf(4)).toBe(-1);
      expect(wrappedGraph.graph[4].upstream_nodes.indexOf(6)).toBe(-1);
    });

    describe('transitive reduction', function() {

      beforeEach(function() {
        wrappedGraph.linkNodes(6,4);
      });

      it('should create link between nodes 4 and 6', function () {
        expect(wrappedGraph.graph[6].downstream_nodes).toContain(4);
        expect(wrappedGraph.graph[4].upstream_nodes).toContain(6);
      });

      it('should break links between nodes 2 and 4', function () {
        expect(wrappedGraph.graph[2].downstream_nodes.indexOf(4)).toBe(-1);
        expect(wrappedGraph.graph[4].upstream_nodes.indexOf(2)).toBe(-1);
      });

      it('should break links between nodes 6 and 7', function () {
        expect(wrappedGraph.graph[6].downstream_nodes.indexOf(7)).toBe(-1);
        expect(wrappedGraph.graph[7].upstream_nodes.indexOf(6)).toBe(-1);
      });

      it('should break links between nodes 6 and 5', function () {
        expect(wrappedGraph.graph[6].downstream_nodes.indexOf(5)).toBe(-1);
        expect(wrappedGraph.graph[5].upstream_nodes.indexOf(6)).toBe(-1);
      });

      describe('delete node', function() {

        beforeEach(function() {
          wrappedGraph.deleteNode(5);
        });

        it('should break link from 3/4 to 5', function () {
          expect(wrappedGraph.graph[4].downstream_nodes.indexOf(5)).toBe(-1);
          expect(wrappedGraph.graph[3].upstream_nodes.indexOf(5)).toBe(-1);
          expect(wrappedGraph.graph[5]).toBeUndefined();
        });

        it('transfer link from 3 to 4', function () {
          wrappedGraph.deleteNode(7);
          expect(wrappedGraph.graph[4].downstream_nodes).toContain(3);
          expect(wrappedGraph.graph[3].upstream_nodes).toContain(4);
        });
      });
    });

  });

});
<|endoftext|>


<commit_before>const $ = require("jquery")
const _ = require("underscore")

if (
  location.pathname.match("/article/") ||
  location.pathname.match("/articles")
) {
  var trackedImpressions = []

  var findVisibleItems = function(articleItems) {
    if (articleItems.length > 0) {
      var items = $(articleItems)
        .filter(function() {
          var viewportTop = $(window).scrollTop()
          var viewportBottom = viewportTop + $(window).height()
          var itemTop = $(this).offset().top
          var itemBottom = itemTop + $(this).outerHeight()
          // Either item top or item bottom is below the top
          // of the browser and above the fold.
          var topInView = itemTop > viewportTop && itemTop < viewportBottom
          var bottomInView =
            itemBottom > viewportTop && itemBottom < viewportBottom

          return topInView || bottomInView
        })
        .map(function() {
          var classList = $(this).prop("classList")
          var articleId = $(this)
            .closest(".article-container")
            .data("id")
          // Editorial Signup
          if (classList.contains("articles-es-cta__form")) {
            if (location.pathname.match("/article/")) {
              return {
                impression_type: "newsletter_signup",
                context_type: "article_fixed",
                article_id: articleId,
                destination_path: null,
                id: "article_fixed",
              }
            } else {
              return {
                impression_type: "newsletter_signup",
                context_type: "magazine_fixed",
                destination_path: null,
                id: "magazine_fixed",
              }
            }
          } else if (classList.contains("article-es-form")) {
            if ($(this).parents(".is-expanded")) {
              return {
                impression_type: "newsletter_signup",
                context_type: "article_fixed",
                article_id: articleId,
                destination_path: null,
                id: "article_fixed",
              }
            }

            // Social
          } else if (classList.contains("article-social")) {
            const article = $(this).closest(".article-content")
            if (
              (article.hasClass("gradient-blurb") &&
                article.hasClass("is-expanded")) ||
              !article.hasClass("gradient-blurb")
            ) {
              return {
                article_id: articleId,
                destination_path: null,
                impression_type: "social",
                context_type: "article_fixed",
                id: "social:" + articleId,
              }
            }
          } else if (classList.contains("article-share-fixed-fullscreen")) {
            var indArticleId = $(this).data("id")
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "social",
              context_type: "article_fixed",
              id: "social:" + indArticleId,
            }

            // Image Set
          } else if (classList.contains("article-section-image-set")) {
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "image_set",
              context_type: "article_fixed",
              id: "image_set:" + articleId + ":" + $(this).data("index"),
            }

            // Related Article
          } else if (classList.contains("article-related-widget")) {
            var related = $(this).find("a")
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "related_article",
              context_type: "article_fixed",
              id: "related_articles:" + trackRelatedLinks(related),
            }

            // Artist Follow
          } else if (classList.contains("artist-follow")) {
            const article = $(this).closest(".article-content")
            if (
              (article.hasClass("gradient-blurb") &&
                article.hasClass("is-expanded")) ||
              !article.hasClass("gradient-blurb")
            ) {
              if ($(this).parent(".image-set-modal__container").length > 0) {
                return {
                  article_id: articleId,
                  destination_path: null,
                  impression_type: "artist_follow",
                  context_type: "article_fixed",
                  context_module: "article_image_set",
                  id: "artist_follow:" + "image set" + ":" + $(this).data("id"),
                }
              } else {
                return {
                  article_id: articleId,
                  destination_path: null,
                  impression_type: "artist_follow",
                  context_type: "article_fixed",
                  context_module: "article_artist_follow",
                  id: "artist_follow:" + articleId + ":" + $(this).data("id"),
                }
              }
            }
          } else if (
            classList.contains("article-section-callout") &&
            $(this)[0].href
          ) {
            var destinationPath = $(this)[0].href.replace(/^.*\/\/[^\/]+/, "")
            return {
              article_id: articleId,
              destination_path: destinationPath,
              impression_type: "article_callout",
              context_type: "article_fixed",
              id: "article_callout:" + articleId + ":" + destinationPath,
            }
          } else {
          }
        })
        .toArray()

      // Don't double track the same impressions
      items = _.filter(items, function(item) {
        return !_.contains(trackedImpressions, item.id)
      })
      trackedImpressions = trackedImpressions.concat(_.pluck(items, "id"))
      // Return only the new impressions
      return items
    } else {
      return []
    }
  }

  var trackImpressions = function() {
    var itemSelectors =
      ".articles-es-cta__form, .article-es-form," +
      ".article-social, .article-share-fixed-fullscreen," +
      ".article-section-image-set," +
      ".article-section-toc, .article-sa-sticky-header.visible," +
      ".article-sa-related," +
      ".artist-follow," +
      ".article-section-callout," +
      ".article-related-widget"

    var items = $(itemSelectors)
    var visibleItems = findVisibleItems(items)
    if (visibleItems.length > 0) {
      visibleItems.map(function(item) {
        trackImpression({ message: "Article Impression", context: item })
      })
    }
  }
  var trackRelatedLinks = function(related) {
    var links = []
    related.map(function(i, link) {
      links.push(link.href.replace(/^.*\/\/[^\/]+/, ""))
    })
    return links
  }

  var trackImpression = function(item) {
    analytics.track(item.message, item.context)
  }

  $(window).on("scroll", _.throttle(trackImpressions, 500))

  analyticsHooks.on("view:image-set-item", function() {
    trackImpressions()
  })
}
<commit_msg>update article impressions<commit_after>const $ = require("jquery")
const _ = require("underscore")

// FIRED IN 2020:
// newsletter_signup
// article_callout
// social
// artist_follow
// related_article
// image_set

if (
  location.pathname.match("/article/") ||
  location.pathname.match("/articles")
) {
  let trackedImpressions = []

  const findVisibleItems = function (articleItems) {
    if (articleItems.length > 0) {
      let items = $(articleItems)
        .filter(function () {
          const viewportTop = $(window).scrollTop()
          const viewportBottom = viewportTop + $(window).height()
          const itemTop = $(this).offset().top
          const itemBottom = itemTop + $(this).outerHeight()
          // Either item top or item bottom is below the top
          // of the browser and above the fold.
          const topInView = itemTop > viewportTop && itemTop < viewportBottom
          const bottomInView =
            itemBottom > viewportTop && itemBottom < viewportBottom

          return topInView || bottomInView
        })
        .map(function () {
          const classList = $(this).prop("classList")
          const articleId = $(this).closest(".article-container").data("id")
          // Editorial Signup
          if (classList.contains("articles-es-cta__form")) {
            if (location.pathname.match("/article/")) {
              return {
                impression_type: "newsletter_signup",
                context_type: "article_fixed",
                article_id: articleId,
                destination_path: null,
                id: "article_fixed",
              }
            } else {
              return {
                impression_type: "newsletter_signup",
                context_type: "magazine_fixed",
                destination_path: null,
                id: "magazine_fixed",
              }
            }
          } else if (classList.contains("article-es-form")) {
            if ($(this).parents(".is-expanded")) {
              return {
                impression_type: "newsletter_signup",
                context_type: "article_fixed",
                article_id: articleId,
                destination_path: null,
                id: "article_fixed",
              }
            }

            // Social
          } else if (classList.contains("article-social")) {
            const article = $(this).closest(".article-content")
            if (
              (article.hasClass("gradient-blurb") &&
                article.hasClass("is-expanded")) ||
              !article.hasClass("gradient-blurb")
            ) {
              return {
                article_id: articleId,
                destination_path: null,
                impression_type: "social",
                context_type: "article_fixed",
                id: "social:" + articleId,
              }
            }
          } else if (classList.contains("article-share-fixed-fullscreen")) {
            const indArticleId = $(this).data("id")
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "social",
              context_type: "article_fixed",
              id: "social:" + indArticleId,
            }

            // Image Set
          } else if (classList.contains("article-section-image-set")) {
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "image_set",
              context_type: "article_fixed",
              id: "image_set:" + articleId + ":" + $(this).data("index"),
            }

            // Related Article
          } else if (classList.contains("article-related-widget")) {
            const related = $(this).find("a")
            return {
              article_id: articleId,
              destination_path: null,
              impression_type: "related_article",
              context_type: "article_fixed",
              id: "related_articles:" + trackRelatedLinks(related),
            }

            // Artist Follow
          } else if (classList.contains("artist-follow")) {
            const article = $(this).closest(".article-content")
            if (
              (article.hasClass("gradient-blurb") &&
                article.hasClass("is-expanded")) ||
              !article.hasClass("gradient-blurb")
            ) {
              if ($(this).parent(".image-set-modal__container").length > 0) {
                return {
                  article_id: articleId,
                  destination_path: null,
                  impression_type: "artist_follow",
                  context_type: "article_fixed",
                  context_module: "article_image_set",
                  id: "artist_follow:" + "image set" + ":" + $(this).data("id"),
                }
              } else {
                return {
                  article_id: articleId,
                  destination_path: null,
                  impression_type: "artist_follow",
                  context_type: "article_fixed",
                  context_module: "article_artist_follow",
                  id: "artist_follow:" + articleId + ":" + $(this).data("id"),
                }
              }
            }
          } else if (
            classList.contains("article-section-callout") &&
            $(this)[0].href
          ) {
            const destinationPath = $(this)[0].href.replace(/^.*\/\/[^\/]+/, "")
            return {
              article_id: articleId,
              destination_path: destinationPath,
              impression_type: "article_callout",
              context_type: "article_fixed",
              id: "article_callout:" + articleId + ":" + destinationPath,
            }
          } else {
            return
          }
        })
        .toArray()

      // Don't double track the same impressions
      items = _.filter(items, function (item) {
        return !_.contains(trackedImpressions, item.id)
      })
      trackedImpressions = trackedImpressions.concat(_.pluck(items, "id"))
      // Return only the new impressions
      return items
    } else {
      return []
    }
  }

  const trackImpressions = function () {
    const itemSelectors =
      ".articles-es-cta__form, .article-es-form," +
      ".article-social, .article-share-fixed-fullscreen," +
      ".article-section-image-set," +
      ".article-sa-sticky-header.visible," +
      ".article-sa-related," +
      ".artist-follow," +
      ".article-section-callout," +
      ".article-related-widget"

    const items = $(itemSelectors)
    const visibleItems = findVisibleItems(items)
    if (visibleItems.length > 0) {
      visibleItems.map(function (item) {
        trackImpression({ message: "Article Impression", context: item })
      })
    }
  }
  const trackRelatedLinks = function (related) {
    let links = []
    related.map(function (i, link) {
      links.push(link.href.replace(/^.*\/\/[^\/]+/, ""))
    })
    return links
  }

  const trackImpression = function (item) {
    analytics.track(item.message, item.context)
  }

  $(window).on("scroll", _.throttle(trackImpressions, 500))

  analyticsHooks.on("view:image-set-item", function () {
    trackImpressions()
  })
}
<|endoftext|>


<commit_before>/*
 * Say Cheese!
 * Lee Machin, 2012
 * http://leemach.in, http://new-bamboo.co.uk
 *
 * Minimal javascript library for integrating a webcam and snapshots into your app.
 *
 * Handles starting up the webcam and rendering the element, and also capturing shots
 * in a separate canvas element.
 *
 * Depends on video and canvas, and of course, getUserMedia. It's unlikely to work
 * on anything but the newest browsers.
 */

SayCheese = (function() {

  var SayCheese;

  navigator.getUserMedia = (navigator.getUserMedia ||
                            navigator.webkitGetUserMedia ||
                            navigator.mozGetUserMedia ||
                            navigator.msGetUserMedia ||
                            false);

  window.AudioContext = (window.AudioContext ||
                         window.webkitAudioContext);

  window.URL = (window.URL ||
                window.webkitURL);

  var ERRORS = {
    NOT_SUPPORTED: 'NOT_SUPPORTED',
    AUDIO_NOT_SUPPORTED: 'AUDIO_NOT_SUPPORTED'
  }
//console.log("Cheeses! I am inside the mousetrap!");
  SayCheese = function SayCheese(element, options) {
    this.video = null,
    this.events = {},
    this.stream = null,
    this.options = {
      snapshots: true,
      audio: false,
      width: 320
    };

    this.setOptions(options);
    this.element = document.querySelector(element);
    return this;
  };

  SayCheese.prototype.on = function on(evt, handler) {
    if (this.events.hasOwnProperty(evt) === false) {
      this.events[evt] = [];
    }

    this.events[evt].push(handler)
  };

  SayCheese.prototype.off = function off(evt, handler) {
    this.events[evt] = this.events[evt].filter(function(h) {
      return h !== handler;
    });
  };

  SayCheese.prototype.trigger = function trigger(evt, data) {
    if (this.events.hasOwnProperty(evt) === false) {
      return false;
    }

    this.events[evt].forEach(function(handler) {
      handler.call(this, data);
    }.bind(this));
  };

  SayCheese.prototype.setOptions = function setOptions(options) {
    // just use naïve, shallow cloning
    for (var opt in options) {
      this.options[opt] = options[opt];
    }
  }

  SayCheese.prototype.getStreamUrl = function getStreamUrl() {
    if (window.URL && window.URL.createObjectURL) {
      return window.URL.createObjectURL(this.stream);
    } else {
      return this.stream;
    }
  };

  SayCheese.prototype.createVideo = function createVideo() {
    var width     = this.options.width,
        height    = 0,
        streaming = false;
    if (!this.video){
      this.video = document.createElement('video');
      return this.trigger('start');
    } else {
      this.video.addEventListener('canplay', function() {
        if (!streaming) {
          height = this.video.videoHeight / (this.video.videoWidth / width);
          this.video.width = width;
          this.video.height = height;
          streaming = true;
          return this.trigger('start');
        }
      }.bind(this), false);
    }
  };

  SayCheese.prototype.linkAudio = function linkAudio() {
    this.audioCtx = new window.AudioContext();
    this.audioStream = this.audioCtx.createMediaStreamSource(this.stream);

    var biquadFilter = this.audioCtx.createBiquadFilter();

    this.audioStream.connect(biquadFilter);
    biquadFilter.connect(this.audioCtx.destination);
  };

  SayCheese.prototype.takeSnapshot = function takeSnapshot(width, height) {
    if (this.options.snapshots === false) {
      return false;
    }

    width  = width || this.video.videoWidth;    // Use either the supplied dimensions, or the size of the actual video preview
    height = height || this.video.videoHeight;

    var snapshot = document.createElement('canvas'),
        ctx      = snapshot.getContext('2d');

    if (this.options.widths) {
      var that = this;
      var snapList = [];
      console.log("Grabbing "+this.options.widths.length+" images");
      var aspectRatio = width/height;
      _.each(this.options.widths, function(w, index, list) {
        snapshot.width = w ;    // Pick up the dimensions from the video frame
        snapshot.height = w / aspectRatio;  // - then it's not clipped (if in portrait mode)
        console.log("Grabbing an image at "+snapshot.width+"x"+snapshot.height);
        ctx.drawImage(that.video, 0, 0, snapshot.width, snapshot.height);

        snapList.push(snapshot.toDataURL('image/png'));
      });
      that.trigger('snapshot', snapList);

    } else {
      console.log("Grabbing a single image at "+width+"x"+height);
      snapshot.width  = width;
      snapshot.height = height;

      ctx.drawImage(this.video, 0, 0, width, height);
// Pass back the dataURL
      this.trigger('snapshot', [snapshot.toDataURL('image/png')]);
    }

    ctx = null;
  };

  // Start up the stream, if possible */
  SayCheese.prototype.start = function start() {

    // fail fast and softly if browser not supported
    if (navigator.getUserMedia === false) {
      this.trigger('error', ERRORS.NOT_SUPPORTED);
      return false;
    }

    var success = function success(stream) {
      this.stream = stream;
      this.createVideo();

      if (navigator.mozGetUserMedia) {
        this.video.mozSrcObject = stream;
      } else {
        this.video.src = this.getStreamUrl();
      }

      if (this.options.audio === true) {
        try {
          this.linkAudio();
        } catch(e) {
          this.trigger('error', ERRORS.AUDIO_NOT_SUPPORTED);
        }
      }

      this.element.appendChild(this.video);
      this.video.play();
    }.bind(this);

    // error is also called when someone denies access
    var error = function error(error) {
      this.trigger('error', error);
    }.bind(this);

    return navigator.getUserMedia({ video: true, audio: this.options.audio }, success, error);
  };

  SayCheese.prototype.stop = function stop() {
    this.stream.stop();

    if (window.URL && window.URL.revokeObjectURL) {
      window.URL.revokeObjectURL(this.video.src);
    }

    return this.trigger('stop');
  };

  return SayCheese;

})();
<commit_msg>trying to re-initialise<commit_after>/*
 * Say Cheese!
 * Lee Machin, 2012
 * http://leemach.in, http://new-bamboo.co.uk
 *
 * Minimal javascript library for integrating a webcam and snapshots into your app.
 *
 * Handles starting up the webcam and rendering the element, and also capturing shots
 * in a separate canvas element.
 *
 * Depends on video and canvas, and of course, getUserMedia. It's unlikely to work
 * on anything but the newest browsers.
 */

SayCheese = (function() {

  var SayCheese;

  navigator.getUserMedia = (navigator.getUserMedia ||
                            navigator.webkitGetUserMedia ||
                            navigator.mozGetUserMedia ||
                            navigator.msGetUserMedia ||
                            false);

  window.AudioContext = (window.AudioContext ||
                         window.webkitAudioContext);

  window.URL = (window.URL ||
                window.webkitURL);

  var ERRORS = {
    NOT_SUPPORTED: 'NOT_SUPPORTED',
    AUDIO_NOT_SUPPORTED: 'AUDIO_NOT_SUPPORTED'
  }
//console.log("Cheeses! I am inside the mousetrap!");
  SayCheese = function SayCheese(element, options) {
    this.video = null,
    this.events = {},
    this.stream = null,
    this.options = {
      snapshots: true,
      audio: false,
      width: 320,
      format: 'dataurl'       // Do this by default, can select 'blob' instead
    };

    this.setOptions(options);
    this.element = document.querySelector(element);
    return this;
  };

  SayCheese.prototype.on = function on(evt, handler) {
    if (this.events.hasOwnProperty(evt) === false) {
      this.events[evt] = [];
    }

    this.events[evt].push(handler)
  };

  SayCheese.prototype.off = function off(evt, handler) {
    this.events[evt] = this.events[evt].filter(function(h) {
      return h !== handler;
    });
  };

  SayCheese.prototype.trigger = function trigger(evt, data) {
    if (this.events.hasOwnProperty(evt) === false) {
      return false;
    }

    this.events[evt].forEach(function(handler) {
      handler.call(this, data);
    }.bind(this));
  };

  SayCheese.prototype.setOptions = function setOptions(options) {
    // just use naïve, shallow cloning
    for (var opt in options) {
      this.options[opt] = options[opt];
    }
  }

  SayCheese.prototype.getStreamUrl = function getStreamUrl() {
    if (window.URL && window.URL.createObjectURL) {
      return window.URL.createObjectURL(this.stream);
    } else {
      return this.stream;
    }
  };

  SayCheese.prototype.createVideo = function createVideo() {
    var width     = this.options.width,
        height    = 0,
        streaming = false;
    if (!this.video){
	  console.log("Creating video element");
      this.video = document.createElement('video');
      return this.trigger('start');
    } else {
      this.video.addEventListener('canplay', function() {
        if (!streaming) {
          height = this.video.videoHeight / (this.video.videoWidth / width);
          this.video.width = width;
          this.video.height = height;
          streaming = true;
          return this.trigger('start');
        }
      }.bind(this), false);
    }
  };

  SayCheese.prototype.linkAudio = function linkAudio() {
    this.audioCtx = new window.AudioContext();
    this.audioStream = this.audioCtx.createMediaStreamSource(this.stream);

    var biquadFilter = this.audioCtx.createBiquadFilter();

    this.audioStream.connect(biquadFilter);
    biquadFilter.connect(this.audioCtx.destination);
  };

  SayCheese.prototype.takeSnapshot = function takeSnapshot(width, height) {
    if (this.options.snapshots === false) {
      return false;
    }

    width  = width || this.video.videoWidth;    // Use either the supplied dimensions, or the size of the actual video preview
    height = height || this.video.videoHeight;

    var snapshot = document.createElement('canvas'),
        ctx      = snapshot.getContext('2d');

    if (this.options.widths) {
      var that = this;
      var snapList = [];
      console.log("Grabbing "+this.options.widths.length+" images");
      var aspectRatio = width/height;
      _.each(this.options.widths, function(w, index, list) {
        snapshot.width = w ;    // Pick up the dimensions from the video frame
        snapshot.height = w / aspectRatio;  // - then it's not clipped (if in portrait mode)
        console.log("Grabbing an image at "+snapshot.width+"x"+snapshot.height);
        ctx.drawImage(that.video, 0, 0, snapshot.width, snapshot.height);
        if (that.options.format === 'dataurl') {
          snapList.push(snapshot.toDataURL('image/png'));
        }
        if (that.options.format === 'blob') {
          snapshot.toBlob(function(blob) {console.log('Saving blob');snapList.push(blob)},'image/png');
        }
      });
      that.trigger('snapshot', snapList);

    } else {
      console.log("Grabbing a single image at "+width+"x"+height);
      snapshot.width  = width;
      snapshot.height = height;

      ctx.drawImage(this.video, 0, 0, width, height);
// Pass back the dataURL
      if (this.options.format === 'dataurl') {
        this.trigger('snapshot', [snapshot.toDataURL('image/png')]);
      }
// Pass back a blob
      if (this.options.format === 'blob') {
        snapshot.toBlob(function(blob) {this.trigger('snapshot', [blob]) },'image/png');
      }
    }

    ctx = null;
  };

  // Start up the stream, if possible */
  SayCheese.prototype.start = function start() {

    // fail fast and softly if browser not supported
    if (navigator.getUserMedia === false) {
      this.trigger('error', ERRORS.NOT_SUPPORTED);
      return false;
    }

    var success = function success(stream) {
      this.stream = stream;
      this.createVideo();

      if (navigator.mozGetUserMedia) {
        this.video.mozSrcObject = stream;
      } else {
        this.video.src = this.getStreamUrl();
      }

      if (this.options.audio === true) {
        try {
          this.linkAudio();
        } catch(e) {
          this.trigger('error', ERRORS.AUDIO_NOT_SUPPORTED);
        }
      }

      this.element.appendChild(this.video);
      this.video.play();
    }.bind(this);

    // error is also called when someone denies access
    var error = function error(error) {
      this.trigger('error', error);
    }.bind(this);

    return navigator.getUserMedia({ video: true, audio: this.options.audio }, success, error);
  };

  SayCheese.prototype.stop = function stop() {
    this.stream.stop();

    if (window.URL && window.URL.revokeObjectURL) {
      window.URL.revokeObjectURL(this.video.src);
    }

    return this.trigger('stop');
  };

  return SayCheese;

})();
<|endoftext|>


<commit_before>'use strict';

define(['superagent', 'uri/URI'], function (superagent, URI) {
    class Printer {
        constructor(url) {
            this.url = new URI(url).normalize().href();
        }

        async print(format, data) {
            const url = new URI(this.url).segment('print').href();
            return (await superagent
                .post(url)
                .send({
                    format, data
                })).body;
        }

        async ports() {
            const url = new URI(this.url).segment('ports').href();
            return (await superagent.get(url)).body;
        }

        async deleteFormat(format) {
            const url = new URI(this.url).segment('db/format').segmentCoded(format).href();
            return (await superagent.del(url)).body;
        }

        async saveFormat(key, value) {
            const url = new URI(this.url).segment('db/format').href();
            return (await superagent.put(url).send({key, value})).body;
        }

        async getFormat(format) {
            const url = new URI(this.url).segment('db/format').segmentCoded(format).href();
            return (await superagent.get(url)).body;
        }

        async getAllFormats() {
            const url = new URI(this.url).segment('db/format').href();
            return (await superagent.get(url)).body;
        }
    }

    return Printer;
});
<commit_msg>Printer: add getFormatNames<commit_after>'use strict';

define(['superagent', 'uri/URI'], function (superagent, URI) {
    class Printer {
        constructor(url) {
            this.url = new URI(url).normalize().href();
        }

        async print(format, data) {
            const url = new URI(this.url).segment('print').href();
            return (await superagent
                .post(url)
                .send({
                    format, data
                })).body;
        }

        async ports() {
            const url = new URI(this.url).segment('ports').href();
            return (await superagent.get(url)).body;
        }

        async deleteFormat(format) {
            const url = new URI(this.url).segment('db/format').segmentCoded(format).href();
            return (await superagent.del(url)).body;
        }

        async saveFormat(key, value) {
            const url = new URI(this.url).segment('db/format').href();
            return (await superagent.put(url).send({key, value})).body;
        }

        async getFormat(format) {
            const url = new URI(this.url).segment('db/format').segmentCoded(format).href();
            return (await superagent.get(url)).body;
        }

        async getAllFormats() {
            const url = new URI(this.url).segment('db/format').href();
            return (await superagent.get(url)).body;
        }

        async getFormatNames() {
            const url = new URI(this.url).segment('db/format/names').href();
            return (await superagent.get(url)).body;
        }
    }

    return Printer;
});
<|endoftext|>


<commit_before>var path = require('path'),
    phantom = require('phantom'),
    Q = require('q'),
    urlParse = require('url-parse'),
    utils = require('./utils'),
    Website = require('./website');

/*
@param ph phantom object
@param array of options
@param array of URLs to render
@param callbackPerUrl Function called after finishing each URL, including the last URL
@param callbackFinal Function called after finishing everything
*/
var renderUrlsToFile = function(ph, options, urls, callbackPerUrl, callbackFinal) {

    utils.log.verbose('');
    utils.log.verbose('Function: renderUrlsToFile');
    utils.log.verbose('');

    var outputFile, outputFilePath;

    outputFilePath = options.paths.output;

    var authSalesforce, collectWebsiteInformation, getFileName, getUserAgent, nextPage, retrievePage, renderViewPort;

    authSalesforce = function(page, callbackAuthSalesforce) {

        var hostName;

        utils.log.log(utils.log.chalk.yellow.bold('\n   ⤷  Salesforce Detected.\n\n      3 Step Authentication Process Required:'));

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('\n     ✔  STEP 1: Redirect'));

            page.evaluate(function() {

                hostName = window.location.hostname;

                console.log('hostName', hostName);
                console.log('INDEX OF', hostName.indexOf('my.salesforce.com'));

                if (hostName.indexOf('my.salesforce.com') > -1) {

                    var btnWrapper = document.getElementById('idp_section_buttons'),
                        btn = btnWrapper.querySelectorAll('button');

                    btn[0].click();
                }

            });
        }, 10000);

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('     ✔  STEP 2: Login'));

            page.evaluate(function(user, password) {

                hostName = window.location.hostname;

                // Populate and submit Login form
                if (hostName.indexOf('smusxath') > -1) {

                    document.Login.USER.value = user;
                    document.Login.PASSWORD.value = password;
                    document.Login.submit();
                }

            }, options.domain.uri.username, options.domain.uri.password);
        }, 20000);

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('     ✔  STEP 3: Success\n'));

            options.isLoggedIn = true;

            return callbackAuthSalesforce();

        }, 30000);

    };

    collectWebsiteInformation = function(page, url){

        var pageExists = false, pageURL;

        // strip potential page event(?pevent or &pevent) from URL
        pageURL = url.substring(0).search(/([?&])\pevent/g) !== -1 ? url.substr(0, url.substring(0).search(/([?&])\pevent/g)) : url;

        // check for page in content collection
        pageExists = options.website.contentCollection.pages.find(function(page) {
            return page.loc === pageURL;
        });

        // collect page content once
        return utils.executeWhen(!pageExists, function() {

            utils.log.verbose('');
            utils.log.verbose('Adding page to content collection: ', url);
            utils.log.verbose('');

            return page.evaluate(function() {

                var data = {
                    headers: [],
                    images: [],
                    links: [],
                    meta: [],
                    scripts: [],
                };

                if (typeof(jQuery) !== 'undefined') {

                    data.meta = {
                        canonical: jQuery('link[rel=canonical]').attr('href'),
                        description: jQuery('meta[name=description]').attr('content'),
                        keywords: jQuery('meta[name=keywords]').attr('content'),
                        title: jQuery(document).find('title').text()
                    };

                    jQuery('h1, h2, h3, h4').map(function() {
                        data.headers.push({
                            class: jQuery(this).attr('class'),
                            src: jQuery(this).attr('src'),
                            text: jQuery(this).text(),
                            type: this.tagName
                        });
                    });

                    jQuery('img').map(function() {
                        data.images.push({
                            alt: jQuery(this).attr('alt'),
                            class: jQuery(this).attr('class'),
                            src: jQuery(this).attr('src')
                        });
                    });

                    jQuery('a[href]').map(function() {
                        data.links.push({
                            class: jQuery(this).attr('class'),
                            href: jQuery(this).attr('href'),
                            html: jQuery(this).html(),
                            rel: jQuery(this).attr('rel')
                        });
                    });

                    jQuery('script').map(function() {
                        data.scripts.push({
                            src: jQuery(this).attr('src'),
                            text: jQuery(this).html()
                        });
                    });
                }

                return data;

            }).then(function(data) {

                // add website content collection
                options.website.contentCollection.pages.push({
                    body: {
                        headers: data.headers,
                        images: data.images,
                        links: data.links
                    },
                    loc: pageURL,
                    meta: data.meta,
                    scripts: data.scripts,
                });

            });
        });

    };

    getFileName = function(_folder, _viewportIndex) {

        var fileName = options.viewports[_viewportIndex].viewport + '.png',
            folderName = new urlParse(_folder, true);

        if (_folder.indexOf('visual.force') > -1) {

            utils.log.verbose('Function: getFileName');
            utils.log.verbose('Salesforce: folderName');
            utils.log.verbose(folderName);

            // Parse Salesforce URL and grab the 'name' get variable
            // Example URL: [domain]dm--cms.na15.visual.force.com/apex/Preview?sname=DevOpsGroup&name=index
            _folder = folderName.query.name + folderName.hash;
        } else {
            // remove ? from folder name
            _folder = _folder.indexOf('?') > -1 ? _folder.substring(0, _folder.indexOf('?')) : _folder;
        }

        if (folderName.query.pevent) {
            fileName = options.viewports[_viewportIndex].viewport + '-event-' + folderName.query.pevent + '.png';
        }

        return path.join(outputFilePath, _folder, fileName);
    };

    getUserAgent = function(viewport) {

        var userAgent = '';

        switch (viewport) {

            case 'iPhone6':
                userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1';
                break;
            default:
                userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1';
                break;
        }

        utils.log.verbose('');
        utils.log.verbose('Function: getUserAgent');
        utils.log.verbose('Value: ', userAgent);
        utils.log.verbose('');

        return userAgent;
    };

    nextPage = function(status, url, file) {
        utils.log.verbose('');
        utils.log.verbose('Function: nextPage');
        utils.log.verbose('');

        callbackPerUrl(status, url, file);
        return retrievePage();
    };

    renderViewPort = function(viewportCounter, page, status, url) {

        if (!!viewportCounter) {

            var viewportIndex = viewportCounter - 1;

            utils.log.log(utils.log.chalk.green.bold('    ✔  Viewport:'), utils.log.chalk.green(options.viewports[viewportIndex].viewport + '-' + options.viewports[viewportIndex].width + 'x' + options.viewports[viewportIndex].height));

            outputFile = getFileName(utils.urlToDir(url), viewportIndex);

            return collectWebsiteInformation(page, url)
                .then(page.evaluate(function(viewportsTotal, viewportsCurrent) {

                    // pass settings to the DOM
                    window.siteshooter = {};
                    window.siteshooter.viewportsTotal = viewportsTotal;
                    window.siteshooter.viewportsCurrent = viewportsCurrent;

                }, options.viewports.length, viewportIndex))
                .then(page.injectJs('inject.js'))
                .then(utils.executeWhen(viewportIndex !== 0, function() {
                    setTimeout(function() {
                        page.render(outputFile).then(function() {

                            page.setting('userAgent', getUserAgent(options.viewports[viewportIndex -1].viewport));

                            page.property('viewportSize', {
                                'width': options.viewports[viewportIndex -1].width,
                                'height': options.viewports[viewportIndex -1].height
                            });

                            page.open(url).then(function() {
                                page.property('onLoadFinished').then(function() {
                                    setTimeout(function() {
                                        renderViewPort(viewportIndex, page, status, url);
                                    }, options.screenshot_options.delay);
                                });
                            });
                        });
                    }, options.screenshot_options.delay);
                }))
                .then(utils.executeWhen(viewportIndex === 0, function() {
                    setTimeout(function() {
                        page.render(outputFile).then(function() {
                            renderViewPort(viewportIndex, page, status, url);
                        });
                    }, options.screenshot_options.delay);

                }));
        } else {

            page.close();

            return nextPage(status, url, outputFile);
        }
    };

    retrievePage = function() {

        utils.log.verbose('');
        utils.log.verbose('Function: retrievePage');
        utils.log.verbose('');

        var folder, url, viewportIndex;

        if (urls.length > 0) {

            viewportIndex = options.viewports.length-1;

            url = urls.shift();
            folder = utils.urlToDir(url);

            ph.createPage().then(function(page) {

                utils.log.verbose('Function: createPage');

                // do we have stored credentials?
                if (options.domain.uri.auth && options.isLoggedIn === false) {

                    utils.log.verbose('\n   ⤷  Setting Authentication');

                    page.setting('userName', options.domain.uri.username);
                    page.setting('password', options.domain.uri.password);
                }

                page.setting('userAgent', getUserAgent(options.viewports[viewportIndex].viewport));

                page.property('viewportSize', {
                    'width': options.viewports[viewportIndex].width,
                    'height': options.viewports[viewportIndex].height
                });

                if (utils.log.isDebug()) {
                    // handles console messages from within an page.evaluate function or inject.js file
                    page.property('onConsoleMessage', function(msg) {
                        console.log(msg);
                    });
                }

                page.open(url).then(function(pageStatus) {

                    utils.log.log('\n   ', utils.log.chalk.underline.yellow(folder));

                    page.property('onLoadFinished').then(function() {

                        // check for Salesforce parts in URL
                        if (url.indexOf('visual.force') > -1 && !options.isLoggedIn) {

                            var hostName = new urlParse(url, true);

                            outputFilePath = path.join(options.paths.output, hostName.host);

                            authSalesforce(page, function() {
                                renderViewPort(options.viewports.length, page, pageStatus, url);
                            });
                        } else if (pageStatus === 'success') {

                            setTimeout(function() {
                                renderViewPort(options.viewports.length, page, pageStatus, url);
                            }, options.screenshot_options.delay);

                        } else {
                            return nextPage(pageStatus, url, outputFile);
                        }

                    });

                });

            });

        } else {
            return callbackFinal();
        }
    };

    return retrievePage();
};

/**
 * Screenshot Object
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {Array}   pages   list of links passed from sitemap.xml file
 * @param  {Object}  options [description]
 */
function Screenshot(parameters) {

    utils.log.verbose('Screenshot: constructor', parameters);

    utils.log.log('');
    utils.log.log(utils.log.chalk.yellow.bold(' ⤷ Generating screenshots'));

    this.pages = parameters.pages;
    this.options = parameters.options;
    this.options.isLoggedIn = false;

    this.options.website = new Website(this.options);

    var exclusions, regExclusions;
    var screenshotExclusions = ['pdf'];

    // format exclusions
    exclusions = screenshotExclusions.join('|');

    // regular expression for exclusions
    regExclusions = new RegExp('\.(' + exclusions + ')', 'i');

    // filter screenshotExclusions
    this.pages = this.pages.filter(function(page) {
        return !page.match(regExclusions);
    });

}

Screenshot.prototype.start = function() {

    var d = Q.defer(),
        options = this.options,
        pages = this.pages,
        phantomOptions = [];

    options.isLoggedIn = false;

    if (options.sitecrawler_options.ignoreInvalidSSL) {
        phantomOptions.push('--ignore-ssl-errors=yes');
    }

    phantom.create(phantomOptions).then(function(ph) {

        renderUrlsToFile(ph, options, pages, function(status, url, file) {

            utils.log.verbose('\nFunction: callbackPerUrl\n');

            if (status !== 'success') {
                return utils.log.verbose('Unable to render ', url);
            } else {
                return utils.log.verbose('Rendered ', url, ' at ', file);
            }
        }, function() {

            utils.log.verbose('\nFunction: callbackFinal\n');

            setTimeout(function() {
                ph.exit();
            }, 2000);

            utils.log.log(utils.log.chalk.yellow.bold('\n ⤷ Collecting website content'));

            options.website.writeContentCollection(function(err) {
                if (err) {
                    d.reject(err);
                } else {

                    utils.log.log(utils.log.chalk.green.bold('\n   ✔ Successfully saved:'), utils.log.chalk.green('content-collection.json'));
                    utils.log.log(utils.log.chalk.green.bold('   ✔ Pages added:'), utils.log.chalk.green(options.website.contentCollection.pages.length));

                    d.resolve();
                }

            });

        });

    });

    return d.promise;
};

module.exports = Screenshot;
<commit_msg>fix(screenshots): Ensure there aren't any duplicate URLs when generating screenshots and content collection.<commit_after>var path = require('path'),
    phantom = require('phantom'),
    Q = require('q'),
    urlParse = require('url-parse'),
    utils = require('./utils'),
    Website = require('./website');

/*
@param ph phantom object
@param array of options
@param array of URLs to render
@param callbackPerUrl Function called after finishing each URL, including the last URL
@param callbackFinal Function called after finishing everything
*/
var renderUrlsToFile = function(ph, options, urls, callbackPerUrl, callbackFinal) {

    utils.log.verbose('');
    utils.log.verbose('Function: renderUrlsToFile');
    utils.log.verbose('');

    var outputFile, outputFilePath;

    outputFilePath = options.paths.output;

    var authSalesforce, collectWebsiteInformation, getFileName, getUserAgent, nextPage, retrievePage, renderViewPort;

    authSalesforce = function(page, callbackAuthSalesforce) {

        var hostName;

        utils.log.log(utils.log.chalk.yellow.bold('\n   ⤷  Salesforce Detected.\n\n      3 Step Authentication Process Required:'));

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('\n     ✔  STEP 1: Redirect'));

            page.evaluate(function() {

                hostName = window.location.hostname;

                console.log('hostName', hostName);
                console.log('INDEX OF', hostName.indexOf('my.salesforce.com'));

                if (hostName.indexOf('my.salesforce.com') > -1) {

                    var btnWrapper = document.getElementById('idp_section_buttons'),
                        btn = btnWrapper.querySelectorAll('button');

                    btn[0].click();
                }

            });
        }, 10000);

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('     ✔  STEP 2: Login'));

            page.evaluate(function(user, password) {

                hostName = window.location.hostname;

                // Populate and submit Login form
                if (hostName.indexOf('smusxath') > -1) {

                    document.Login.USER.value = user;
                    document.Login.PASSWORD.value = password;
                    document.Login.submit();
                }

            }, options.domain.uri.username, options.domain.uri.password);
        }, 20000);

        setTimeout(function() {

            utils.log.log(utils.log.chalk.green.bold('     ✔  STEP 3: Success\n'));

            options.isLoggedIn = true;

            return callbackAuthSalesforce();

        }, 30000);

    };

    collectWebsiteInformation = function(page, url){

        var pageExists = false, pageURL;

        // strip potential page event(?pevent or &pevent) from URL
        pageURL = url.substring(0).search(/([?&])\pevent/g) !== -1 ? url.substr(0, url.substring(0).search(/([?&])\pevent/g)) : url;

        // check for page in content collection
        pageExists = options.website.contentCollection.pages.find(function(page) {
            return page.loc === pageURL;
        });

        // collect page content once
        return utils.executeWhen(!pageExists, function() {

            utils.log.verbose('');
            utils.log.verbose('Adding page to content collection: ', url);
            utils.log.verbose('');

            return page.evaluate(function() {

                var data = {
                    headers: [],
                    images: [],
                    links: [],
                    meta: [],
                    scripts: [],
                };

                if (typeof(jQuery) !== 'undefined') {

                    data.meta = {
                        canonical: jQuery('link[rel=canonical]').attr('href'),
                        description: jQuery('meta[name=description]').attr('content'),
                        keywords: jQuery('meta[name=keywords]').attr('content'),
                        title: jQuery(document).find('title').text()
                    };

                    jQuery('h1, h2, h3, h4').map(function() {
                        data.headers.push({
                            class: jQuery(this).attr('class'),
                            src: jQuery(this).attr('src'),
                            text: jQuery(this).text(),
                            type: this.tagName
                        });
                    });

                    jQuery('img').map(function() {
                        data.images.push({
                            alt: jQuery(this).attr('alt'),
                            class: jQuery(this).attr('class'),
                            src: jQuery(this).attr('src')
                        });
                    });

                    jQuery('a[href]').map(function() {
                        data.links.push({
                            class: jQuery(this).attr('class'),
                            href: jQuery(this).attr('href'),
                            html: jQuery(this).html(),
                            rel: jQuery(this).attr('rel')
                        });
                    });

                    jQuery('script').map(function() {
                        data.scripts.push({
                            src: jQuery(this).attr('src'),
                            text: jQuery(this).html()
                        });
                    });
                }

                return data;

            }).then(function(data) {

                // add website content collection
                options.website.contentCollection.pages.push({
                    body: {
                        headers: data.headers,
                        images: data.images,
                        links: data.links
                    },
                    loc: pageURL,
                    meta: data.meta,
                    scripts: data.scripts,
                });

            });
        });

    };

    getFileName = function(_folder, _viewportIndex) {

        var fileName = options.viewports[_viewportIndex].viewport + '.png',
            folderName = new urlParse(_folder, true);

        if (_folder.indexOf('visual.force') > -1) {

            utils.log.verbose('Function: getFileName');
            utils.log.verbose('Salesforce: folderName');
            utils.log.verbose(folderName);

            // Parse Salesforce URL and grab the 'name' get variable
            // Example URL: [domain]dm--cms.na15.visual.force.com/apex/Preview?sname=DevOpsGroup&name=index
            _folder = folderName.query.name + folderName.hash;
        } else {
            // remove ? from folder name
            _folder = _folder.indexOf('?') > -1 ? _folder.substring(0, _folder.indexOf('?')) : _folder;
        }

        if (folderName.query.pevent) {
            fileName = options.viewports[_viewportIndex].viewport + '-event-' + folderName.query.pevent + '.png';
        }

        return path.join(outputFilePath, _folder, fileName);
    };

    getUserAgent = function(viewport) {

        var userAgent = '';

        switch (viewport) {

            case 'iPhone6':
                userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1';
                break;
            default:
                userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1';
                break;
        }

        utils.log.verbose('');
        utils.log.verbose('Function: getUserAgent');
        utils.log.verbose('Value: ', userAgent);
        utils.log.verbose('');

        return userAgent;
    };

    nextPage = function(status, url, file) {
        utils.log.verbose('');
        utils.log.verbose('Function: nextPage');
        utils.log.verbose('');

        callbackPerUrl(status, url, file);
        return retrievePage();
    };

    renderViewPort = function(viewportCounter, page, status, url) {

        if (!!viewportCounter) {

            var viewportIndex = viewportCounter - 1;

            utils.log.log(utils.log.chalk.green.bold('    ✔  Viewport:'), utils.log.chalk.green(options.viewports[viewportIndex].viewport + '(' + options.viewports[viewportIndex].width + 'x' + options.viewports[viewportIndex].height + ')'));

            outputFile = getFileName(utils.urlToDir(url), viewportIndex);

            return collectWebsiteInformation(page, url)
                .then(page.evaluate(function(viewportsTotal, viewportsCurrent) {

                    // pass settings to the DOM
                    window.siteshooter = {};
                    window.siteshooter.viewportsTotal = viewportsTotal;
                    window.siteshooter.viewportsCurrent = viewportsCurrent;

                }, options.viewports.length, viewportIndex))
                .then(page.injectJs('inject.js'))
                .then(utils.executeWhen(viewportIndex !== 0, function() {
                    setTimeout(function() {
                        page.render(outputFile).then(function() {

                            page.setting('userAgent', getUserAgent(options.viewports[viewportIndex -1].viewport));

                            page.property('viewportSize', {
                                'width': options.viewports[viewportIndex -1].width,
                                'height': options.viewports[viewportIndex -1].height
                            });

                            page.open(url).then(function() {
                                page.property('onLoadFinished').then(function() {
                                    setTimeout(function() {
                                        renderViewPort(viewportIndex, page, status, url);
                                    }, options.screenshot_options.delay);
                                });
                            });
                        });
                    }, options.screenshot_options.delay);
                }))
                .then(utils.executeWhen(viewportIndex === 0, function() {
                    setTimeout(function() {
                        page.render(outputFile).then(function() {
                            renderViewPort(viewportIndex, page, status, url);
                        });
                    }, options.screenshot_options.delay);

                }));
        } else {

            page.close();

            return nextPage(status, url, outputFile);
        }
    };

    retrievePage = function() {

        utils.log.verbose('');
        utils.log.verbose('Function: retrievePage');
        utils.log.verbose('');

        var folder, url, viewportIndex;

        if (urls.length > 0) {

            viewportIndex = options.viewports.length-1;

            url = urls.shift();
            folder = utils.urlToDir(url);

            ph.createPage().then(function(page) {

                utils.log.verbose('Function: createPage');

                // do we have stored credentials?
                if (options.domain.uri.auth && options.isLoggedIn === false) {

                    utils.log.verbose('\n   ⤷  Setting Authentication');

                    page.setting('userName', options.domain.uri.username);
                    page.setting('password', options.domain.uri.password);
                }

                page.setting('userAgent', getUserAgent(options.viewports[viewportIndex].viewport));

                page.property('viewportSize', {
                    'width': options.viewports[viewportIndex].width,
                    'height': options.viewports[viewportIndex].height
                });

                if (utils.log.isDebug()) {
                    // handles console messages from within an page.evaluate function or inject.js file
                    page.property('onConsoleMessage', function(msg) {
                        console.log(msg);
                    });
                }

                page.open(url).then(function(pageStatus) {

                    utils.log.log('\n   ', utils.log.chalk.underline.yellow(folder));

                    page.property('onLoadFinished').then(function() {

                        // check for Salesforce parts in URL
                        if (url.indexOf('visual.force') > -1 && !options.isLoggedIn) {

                            var hostName = new urlParse(url, true);

                            outputFilePath = path.join(options.paths.output, hostName.host);

                            authSalesforce(page, function() {
                                renderViewPort(options.viewports.length, page, pageStatus, url);
                            });
                        } else if (pageStatus === 'success') {

                            setTimeout(function() {
                                renderViewPort(options.viewports.length, page, pageStatus, url);
                            }, options.screenshot_options.delay);

                        } else {
                            return nextPage(pageStatus, url, outputFile);
                        }

                    });

                });

            });

        } else {
            return callbackFinal();
        }
    };

    return retrievePage();
};

/**
 * Screenshot Object
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {Array}   pages   list of links passed from sitemap.xml file
 * @param  {Object}  options [description]
 */
function Screenshot(parameters) {

    utils.log.verbose('Screenshot: constructor', parameters);

    utils.log.log('');
    utils.log.log(utils.log.chalk.yellow.bold(' ⤷ Generating screenshots'));

    this.pages = parameters.pages;
    this.options = parameters.options;
    this.options.isLoggedIn = false;

    this.options.website = new Website(this.options);

    var exclusions, regExclusions;
    var screenshotExclusions = ['pdf'];

    // format exclusions
    exclusions = screenshotExclusions.join('|');

    // regular expression for exclusions
    regExclusions = new RegExp('\.(' + exclusions + ')', 'i');

    // remove trailing slashes, remove duplicates, and filter screenshotExclusions
    this.pages = this.pages.map(utils.urlRemoveTrailingSlash).filter(function(val, ind, pages) {
        return pages.indexOf(val) === ind;
    }).filter(function(page) {
        return !page.match(regExclusions);
    });

}

Screenshot.prototype.start = function() {

    var d = Q.defer(),
        options = this.options,
        pages = this.pages,
        phantomOptions = [];

    options.isLoggedIn = false;

    if (options.sitecrawler_options.ignoreInvalidSSL) {
        phantomOptions.push('--ignore-ssl-errors=yes');
    }

    phantom.create(phantomOptions).then(function(ph) {

        renderUrlsToFile(ph, options, pages, function(status, url, file) {

            utils.log.verbose('\nFunction: callbackPerUrl\n');

            if (status !== 'success') {
                return utils.log.verbose('Unable to render ', url);
            } else {
                return utils.log.verbose('Rendered ', url, ' at ', file);
            }
        }, function() {

            utils.log.verbose('\nFunction: callbackFinal\n');

            setTimeout(function() {
                ph.exit();
            }, 2000);

            utils.log.log(utils.log.chalk.yellow.bold('\n ⤷ Collecting website content'));

            options.website.writeContentCollection(function(err) {
                if (err) {
                    d.reject(err);
                } else {

                    utils.log.log(utils.log.chalk.green.bold('\n   ✔ Successfully saved:'), utils.log.chalk.green('content-collection.json'));
                    utils.log.log(utils.log.chalk.green.bold('   ✔ Pages added:'), utils.log.chalk.green(options.website.contentCollection.pages.length));

                    d.resolve();
                }

            });

        });

    });

    return d.promise;
};

module.exports = Screenshot;
<|endoftext|>


<commit_before>import marked from 'marked'
import hljs from 'highlight.js'

marked.setOptions({
  highlight: function (code, lang) {
    if (hljs.getLanguage(lang)) {
      return hljs.highlight(lang, code).value
    } else {
      return hljs.highlightAuto(code).value
    }
  }
})

export default marked
<commit_msg>[前端-后台管理] prev标签增加默认class<commit_after>import marked from 'marked'
import hljs from 'highlight.js'

marked.setOptions({
  highlight: function (code, lang) {
    if (hljs.getLanguage(lang)) {
      return hljs.highlight(lang, code).value
    } else {
      return hljs.highlightAuto(code).value
    }
  }
})

const Marked = text => {
  var tok = marked.lexer(text)
  text = marked.parser(tok).replace(/<pre>/ig, '<pre class="hljs">')
  return text
}

export default Marked
<|endoftext|>


<commit_before>'use strict';

(function() {
  angular.module('impactApp.auth')
    .run(function($rootScope, $state, Auth) {
      // Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
      $rootScope.$on('$stateChangeStart', function(event, next, nextParams) {
        if (typeof next.redirectTo !== 'undefined') {
          event.preventDefault();
          if (typeof next.redirectTo === 'string') {
            $state.go(next.redirectTo, nextParams);
          } else {
            var params = _.assign(nextParams, next.redirectTo.params);
            $state.go(next.redirectTo.url, params);
          }

          return;
        }

        if (typeof next.authenticate !== 'undefined') {
          Auth.isLoggedIn(_.noop).then(is => {
            if (is) {
              return;
            }

            event.preventDefault();
            $state.go('login');
          });

          return;
        }
      });
    });
})();
<commit_msg>Only redirect if needed<commit_after>'use strict';

(function() {
  angular.module('impactApp.auth')
    .run(function($rootScope, $state, Auth) {
      // Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
      $rootScope.$on('$stateChangeStart', function(event, next, nextParams) {
        if (typeof next.authenticate !== 'undefined') {
          Auth.isLoggedIn(_.noop).then(is => {
            if (is) {
              if (typeof next.redirectTo !== 'undefined') {
                event.preventDefault();

                if (typeof next.redirectTo === 'string') {
                  $state.go(next.redirectTo, nextParams);
                } else {
                  let toParams = _.assign(nextParams, next.redirectTo.params);
                  $state.go(next.redirectTo.url, toParams);
                }
              }

              return;
            }

            event.preventDefault();
            $state.go('login');
          });

          return;
        }

        if (typeof next.redirectTo !== 'undefined') {
          event.preventDefault();

          if (typeof next.redirectTo === 'string') {
            $state.go(next.redirectTo, nextParams);
          } else {
            let toParams = _.assign(nextParams, next.redirectTo.params);
            $state.go(next.redirectTo.url, toParams);
          }
        }

        return;
      });
    });
})();
<|endoftext|>


<commit_before>(function(undefined) {
    var viewType = "textbox",
		cachedElement;

    function setValue(event){
        var input = this;
        var matchFail = function(){
            $(input).addClass('error');
        };

        $(input).removeClass('error');

        if ($(input).attr("type") === "number") {
            window.gaffa.propertyUpdaters.string(input.viewModel, input.viewModel.value, parseFloat($(input).val()), matchFail);
        } else {
            window.gaffa.propertyUpdaters.string(input.viewModel, input.viewModel.value, $(input).val(), matchFail);
        }
    }

    var updateValue = window.gaffa.propertyUpdaters.string("value", function(viewModel, value){

        var element = viewModel.renderedElement,
            caretPosition = 0,
            $element = $(element),
            originalValue = $element.val(),
            hasCaret = $element.is(':focus'); //this is only necissary because IE10 is a pile of crap (i know what a supprise)

        if(originalValue === value){
            return;
        }

        // Inspiration taken from http://stackoverflow.com/questions/2897155/get-caret-position-within-an-text-input-field
        // but WOW is that some horrendous code! Max <http://stackoverflow.com/users/43677/max> should feel bad.
        // hungarian notation in JS, mixing between explicit and implicit braces in an if-else
        // spaces between function and call parenthesies...
        // And you couldn't afford yo type 'Position'?? wtf, was 'ition' that much more that you couldn't type it?!
        // iSel.... WHAT THE FUCK IS THAT? I assumed selection, but FUCK, just type 'selection'!
        // Just wow.
        if(hasCaret){
            if (document.selection) {
                var selection = document.selection.createRange();
                selection.moveStart('character', -element.value.length);
                caretPosition = selection.text.length;
            }
            else if (element.selectionStart || element.selectionStart == '0'){
                caretPosition = element.selectionStart;
            }
        }

        $element.val(value);

        if(hasCaret){
            if(element.createTextRange) {
                var range = element.createTextRange();

                range.move('character', caretPosition);

                range.select();
            }
            if(element.selectionStart) {
                element.setSelectionRange(caretPosition, caretPosition);
            }
        }

    });

    var updateSubType = window.gaffa.propertyUpdaters.string("subType", function(viewModel, value){
        $(viewModel.renderedElement).attr('type', value);
    });

    var updatePlaceholder = window.gaffa.propertyUpdaters.string("placeholder", function(viewModel, value){
        $(viewModel.renderedElement).attr('placeholder', value);
    });

    var updateDisabled = window.gaffa.propertyUpdaters.bool("disabled", function(viewModel, value){
        if (value){
            viewModel.renderedElement.setAttribute('disabled', 'disabled');
        }else{
            viewModel.renderedElement.removeAttribute('disabled');
        }
    });

    function Textbox(){}
    Textbox = gaffa.createSpec(Textbox, gaffa.View);
    Textbox.prototype.type = viewType;

    Textbox.prototype.render = function(){
        var classes = viewType;

        var renderedElement = cachedElement || (cachedElement = document.createElement('input'));

        if (this.name.value) {
            renderedElement.setAttribute("name", this.name.value)
        }

        renderedElement = $(renderedElement.cloneNode(true)).addClass(classes)[0];

        $(renderedElement).on(this.updateEventName || "change", setValue);

        this.renderedElement = renderedElement;

        this.__super__.render.apply(this, arguments);
    };

    Textbox.prototype.value = new gaffa.Property(updateValue);

    Textbox.prototype.subType = new gaffa.Property(updateSubType);

    Textbox.prototype.placeholder = new gaffa.Property(updatePlaceholder);

    Textbox.prototype.disabled = new gaffa.Property(updateDisabled);

    gaffa.views[viewType] = Textbox;

})();<commit_msg>removing redundant code<commit_after>(function(undefined) {
    var viewType = "textbox",
		cachedElement;

    function setValue(event){
        var input = this;
        var matchFail = function(){
            $(input).addClass('error');
        };

        $(input).removeClass('error');

        if ($(input).attr("type") === "number") {
            window.gaffa.propertyUpdaters.string(input.viewModel, input.viewModel.value, parseFloat($(input).val()), matchFail);
        } else {
            window.gaffa.propertyUpdaters.string(input.viewModel, input.viewModel.value, $(input).val(), matchFail);
        }
    }

    var updateValue = window.gaffa.propertyUpdaters.string("value", function(viewModel, value){

        var element = viewModel.renderedElement,
            caretPosition = 0,
            $element = $(element),
            originalValue = $element.val(),
            hasCaret = $element.is(':focus'); //this is only necissary because IE10 is a pile of crap (i know what a supprise)

        if(originalValue === value){
            return;
        }

        // Inspiration taken from http://stackoverflow.com/questions/2897155/get-caret-position-within-an-text-input-field
        // but WOW is that some horrendous code! Max <http://stackoverflow.com/users/43677/max> should feel bad.
        // hungarian notation in JS, mixing between explicit and implicit braces in an if-else
        // spaces between function and call parenthesies...
        // And you couldn't afford yo type 'Position'?? wtf, was 'ition' that much more that you couldn't type it?!
        // iSel.... WHAT THE FUCK IS THAT? I assumed selection, but FUCK, just type 'selection'!
        // Just wow.
        if(hasCaret){
            if (document.selection) {
                var selection = document.selection.createRange();
                selection.moveStart('character', -element.value.length);
                caretPosition = selection.text.length;
            }
            else if (element.selectionStart || element.selectionStart == '0'){
                caretPosition = element.selectionStart;
            }
        }

        $element.val(value);

        if(hasCaret){
            if(element.createTextRange) {
                var range = element.createTextRange();

                range.move('character', caretPosition);

                range.select();
            }
            if(element.selectionStart) {
                element.setSelectionRange(caretPosition, caretPosition);
            }
        }

    });

    var updateSubType = window.gaffa.propertyUpdaters.string("subType", function(viewModel, value){
        $(viewModel.renderedElement).attr('type', value);
    });

    var updatePlaceholder = window.gaffa.propertyUpdaters.string("placeholder", function(viewModel, value){
        $(viewModel.renderedElement).attr('placeholder', value);
    });

    var updateDisabled = window.gaffa.propertyUpdaters.bool("disabled", function(viewModel, value){
        if (value){
            viewModel.renderedElement.setAttribute('disabled', 'disabled');
        }else{
            viewModel.renderedElement.removeAttribute('disabled');
        }
    });

    function Textbox(){}
    Textbox = gaffa.createSpec(Textbox, gaffa.View);
    Textbox.prototype.type = viewType;

    Textbox.prototype.render = function(){
        var classes = viewType;

        var renderedElement = cachedElement || (cachedElement = document.createElement('input'));

        renderedElement = $(renderedElement.cloneNode(true)).addClass(classes)[0];

        $(renderedElement).on(this.updateEventName || "change", setValue);

        this.renderedElement = renderedElement;

        this.__super__.render.apply(this, arguments);
    };

    Textbox.prototype.value = new gaffa.Property(updateValue);

    Textbox.prototype.subType = new gaffa.Property(updateSubType);

    Textbox.prototype.placeholder = new gaffa.Property(updatePlaceholder);

    Textbox.prototype.disabled = new gaffa.Property(updateDisabled);

    gaffa.views[viewType] = Textbox;

})();<|endoftext|>


<commit_before>import React, { PropTypes as pt } from 'react';
import classNames from 'classnames';
import composeRecipeContainer from './RecipeContainer.js';
import { runRecipe } from '../../selfrepair/self_repair_runner.js';
import NormandyDriver from '../../selfrepair/normandy_driver.js';
import Mozilla from '../../selfrepair/uitour.js';

class RecipePreview extends React.Component {
  static propTypes = {
    recipe: pt.object.isRequired,
  }

  constructor(props) {
    super(props);

    this.state = {
      status: 'start',
      error: null,
      errorHelp: null,
    };
  }

  componentWillMount() {
    this.attemptPreview();
  }

  componentDidUpdate() {
    this.attemptPreview();
  }

  attemptPreview() {
    const { recipe } = this.props;
    const { status } = this.state;
    const driver = new NormandyDriver();
    driver.registerCallbacks();

    if (recipe && status === 'start') {
      this.pingUITour();
    } else if (recipe && status === 'uitourFound') {
      this.setState({
        status: 'attempting',
      });

      runRecipe(recipe, driver, { testing: true }).then(() => {
        this.setState({
          status: 'executed',
        });
      })
      .catch(error => {
        this.setState({
          status: 'error',
          error: `Error running recipe: ${error}`,
        });
      });
    }
  }

  pingUITour() {
    const timeout = setTimeout(() => {
      const url = new URL(window.location);
      const prefName = 'browser.uitour.testingOrigins';
      const prefValue = `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : ':'}`;
      this.setState({
        status: 'error',
        error: 'UITour unavailable',
        errorHelp: (

            Make sure the pref {prefName} includes {prefValue}.

        ),
      });
    }, 1000);

    Mozilla.UITour.ping(() => {
      clearTimeout(timeout);
      this.setState({
        status: 'uitourFound',
      });
    });
  }

  render() {
    const { recipe } = this.props;
    const { status, error, errorHelp } = this.state;
    if (recipe) {
      return (

Previewing {recipe.name}...

Action Type: {recipe.action}

            <ExecuteStatus status={status} error={error} errorHelp={errorHelp} />

      );
    }
    return null;
  }
}

function ExecuteStatus({ status, error, errorHelp }) {
  let statusColor = 'grey';
  let statusIcon = 'circle';
  let statusText = 'Unknown';

  switch (status) {
    case 'start':
    case 'uitourFound': {
      statusColor = 'blue';
      statusText = 'Initializing';
      break;
    }
    case 'attempting': {
      statusColor = 'green';
      statusIcon = 'fa-circle-thin';
      statusText = 'Running recipe';
      break;
    }
    case 'executed': {
      statusColor = 'green';
      statusText = 'Recipe executed';
      break;
    }
    case 'error': {
      statusColor = 'red';
      statusIcon = 'circle-thin';
      statusText = {error} {errorHelp &&
{errorHelp}
};
      break;
    }
    default: {
      throw new Error(`Unexpected execution status "${status}"`);
    }
  }

  return (

      <i className={`fa fa-${statusIcon} pre`} />
      {statusText}

  );
}
ExecuteStatus.propTypes = {
  status: pt.string.isRequired,
  error: pt.node,
  errorHelp: pt.node,
};

export default composeRecipeContainer(RecipePreview);
<commit_msg>Remove double fa- prefix<commit_after>import React, { PropTypes as pt } from 'react';
import classNames from 'classnames';
import composeRecipeContainer from './RecipeContainer.js';
import { runRecipe } from '../../selfrepair/self_repair_runner.js';
import NormandyDriver from '../../selfrepair/normandy_driver.js';
import Mozilla from '../../selfrepair/uitour.js';

class RecipePreview extends React.Component {
  static propTypes = {
    recipe: pt.object.isRequired,
  }

  constructor(props) {
    super(props);

    this.state = {
      status: 'start',
      error: null,
      errorHelp: null,
    };
  }

  componentWillMount() {
    this.attemptPreview();
  }

  componentDidUpdate() {
    this.attemptPreview();
  }

  attemptPreview() {
    const { recipe } = this.props;
    const { status } = this.state;
    const driver = new NormandyDriver();
    driver.registerCallbacks();

    if (recipe && status === 'start') {
      this.pingUITour();
    } else if (recipe && status === 'uitourFound') {
      this.setState({
        status: 'attempting',
      });

      runRecipe(recipe, driver, { testing: true }).then(() => {
        this.setState({
          status: 'executed',
        });
      })
      .catch(error => {
        this.setState({
          status: 'error',
          error: `Error running recipe: ${error}`,
        });
      });
    }
  }

  pingUITour() {
    const timeout = setTimeout(() => {
      const url = new URL(window.location);
      const prefName = 'browser.uitour.testingOrigins';
      const prefValue = `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : ':'}`;
      this.setState({
        status: 'error',
        error: 'UITour unavailable',
        errorHelp: (

            Make sure the pref {prefName} includes {prefValue}.

        ),
      });
    }, 1000);

    Mozilla.UITour.ping(() => {
      clearTimeout(timeout);
      this.setState({
        status: 'uitourFound',
      });
    });
  }

  render() {
    const { recipe } = this.props;
    const { status, error, errorHelp } = this.state;
    if (recipe) {
      return (

Previewing {recipe.name}...

Action Type: {recipe.action}

            <ExecuteStatus status={status} error={error} errorHelp={errorHelp} />

      );
    }
    return null;
  }
}

function ExecuteStatus({ status, error, errorHelp }) {
  let statusColor = 'grey';
  let statusIcon = 'circle';
  let statusText = 'Unknown';

  switch (status) {
    case 'start':
    case 'uitourFound': {
      statusColor = 'blue';
      statusText = 'Initializing';
      break;
    }
    case 'attempting': {
      statusColor = 'green';
      statusIcon = 'circle-thin';
      statusText = 'Running recipe';
      break;
    }
    case 'executed': {
      statusColor = 'green';
      statusText = 'Recipe executed';
      break;
    }
    case 'error': {
      statusColor = 'red';
      statusIcon = 'circle-thin';
      statusText = {error} {errorHelp &&
{errorHelp}
};
      break;
    }
    default: {
      throw new Error(`Unexpected execution status "${status}"`);
    }
  }

  return (

      <i className={`fa fa-${statusIcon} pre`} />
      {statusText}

  );
}
ExecuteStatus.propTypes = {
  status: pt.string.isRequired,
  error: pt.node,
  errorHelp: pt.node,
};

export default composeRecipeContainer(RecipePreview);
<|endoftext|>