<commit_before>
(function() {
  var margin = { top: 30, left: 100, right: 30, bottom: 30},
  height = 800 - margin.top - margin.bottom,
  width = 780 - margin.left - margin.right;

  console.log("Building chart 1");

  // Create a time parser
  var parse = d3.timeParse("%m/%d/%y")

  // Create your scales, but ONLY give them a range
  // (you'll set the domain once you read in your data)
  var xPositionScale = d3.scaleLinear().range([0, width]);
  var yPositionScale = d3.scaleLinear().range([height, 0])
  var line = d3.line()
    .x(function(d) {
      return xPositionScale(d.Score);
      })
    .y(function(d) {
      return yPositionScale(d.datetime);
      })
    .curve(d3.curveMonotoneX)

  var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
//     .offset(function() {
//       return [this.getBBox().height / 2, 0]
// })

    .html(function(d) {
      return "" + d.month + "
" + d.trump + ""
    })

  var svg = d3.select("#project1-chart1")
    .append("svg")
    .attr("height", height + margin.top + margin.bottom)
    .attr("width", width + margin.left + margin.right)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg.call(tip);
  console.log(tip)

  d3.queue()
    .defer(d3.csv, "project1_dataset.csv", function(d) {
      // While we're reading the data in, parse each date
      // into a datetime object so it isn't just a string
      // save it as 'd.datetime'
      // d.datetime is now your 'useful' date, you can ignore
      // d.Date. Feel free to use console.log to check it out.
      d.datetime = parse(d.Week);
      console.log(d.datetime)
      console.log(d.Week)
      //console.log(d.datetime)

      d.Score = +d.score;
      console.log(d.Score)
      //console.log(d.Close)
      return d;

    })
    .await(ready);

    function ready(error, datapoints) {

    console.log(datapoints)

    // Get the max and min of datetime and Close,
    // then use that to set the domain of your scale

    // NOTE:I've done it for the datetime, you do it for the close price
    var minDatetime = d3.min(datapoints, function(d) { return d.datetime });
    var maxDatetime = d3.max(datapoints, function(d) { return d.datetime });
    yPositionScale.domain([maxDatetime, minDatetime])

    var minClosetime = d3.min(datapoints, function(d) { return d.Score });
    var maxClosetime = d3.max(datapoints, function(d) { return d.Score });
    xPositionScale.domain([minClosetime, maxClosetime])

    // Draw your dots
    svg.selectAll(".trumpcircles")
      .data(datapoints)
      .enter().append("circle")
        .attr("class", "trumpcircles")
        .attr("r", function(d) {
          if (d.trump != "") {return "6"}
          else {return "none"}
        })
        .attr("stroke", function(d) {
          if (d.Score > 1) {return "red"}
          else {return "none"}
        })
        .attr("stroke-width", 2)
        .attr("fill", "white")
        .attr("cx", function(d) {
          return xPositionScale(d.Score)
        })
        .attr("cy", function(d) {
          return yPositionScale(d.datetime)
        })

        .on('mouseover', function(d,i) {
          tip.show(d,i)
          svg.selectAll(".trumpcircles")
          .filter(function(e) {
            return(e.datetime != d.datetime)
          })
          .transition()
          .style("opacity", 0.2)

        })

        .on('mouseout', function(d,i) {
          tip.hide(d,i)
          svg.selectAll(".trumpcircles")
          .filter(function(e) {
            return(e.datetime != d.datetime)
          })
          .transition()
          .style("opacity", 1)
        })
    // Draw your SINGLE line (but remember it isn't called a line)
    svg.append("path")
      .datum(datapoints)
      .attr("d", line)
      .attr("fill", "none")
      .attr("stroke", "red")
      .attr("stroke-width", 3)

    // Add your axes - I've added the x axis for you
    // Notice that xAxis gets a .tickFormat to format the dates!
    // You won't use this again unless you're doing time again.
      var xAxis = d3.axisBottom(xPositionScale);
      svg.append("g")
        .attr("class", "axis x-axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

      var yAxis = d3.axisLeft(yPositionScale).tickFormat(d3.timeFormat("%Y"));
      svg.append("g")
        .attr("class", "axis y-axis")
        .call(yAxis);

    }
})();
<commit_msg>Update project1-chart1.js<commit_after>
(function() {
  var margin = { top: 30, left: 100, right: 30, bottom: 30},
  height = 800 - margin.top - margin.bottom,
  width = 780 - margin.left - margin.right;

  console.log("Building chart 1");

  // Create a time parser
  var parse = d3.timeParse("%m/%d/%y")

  // Create your scales, but ONLY give them a range
  // (you'll set the domain once you read in your data)
  var xPositionScale = d3.scaleLinear().range([0, width]);
  var yPositionScale = d3.scaleLinear().range([height, 0])
  var line = d3.line()
    .x(function(d) {
      return xPositionScale(d.Score);
      })
    .y(function(d) {
      return yPositionScale(d.datetime);
      })
    .curve(d3.curveMonotoneX)

  var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-10, 0])
//     .offset(function() {
//       return [this.getBBox().height / 2, 0]
// })

    .html(function(d) {
      return "" + d.month + "
" + d.trump + ""
    })

  var svg = d3.select("#project1-chart1")
    .append("svg")
    .attr("height", height + margin.top + margin.bottom)
    .attr("width", width + margin.left + margin.right)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg.call(tip);
  console.log(tip)

  d3.queue()
    .defer(d3.csv, "project1_dataset.csv", function(d) {
      // While we're reading the data in, parse each date
      // into a datetime object so it isn't just a string
      // save it as 'd.datetime'
      // d.datetime is now your 'useful' date, you can ignore
      // d.Date. Feel free to use console.log to check it out.
      d.datetime = parse(d.Week);
      console.log(d.datetime)
      console.log(d.Week)
      //console.log(d.datetime)

      d.Score = +d.score;
      console.log(d.Score)
      //console.log(d.Close)
      return d;

    })
    .await(ready);

    function ready(error, datapoints) {

    console.log(datapoints)

    // Get the max and min of datetime and Close,
    // then use that to set the domain of your scale

    // NOTE:I've done it for the datetime, you do it for the close price
    var minDatetime = d3.min(datapoints, function(d) { return d.datetime });
    var maxDatetime = d3.max(datapoints, function(d) { return d.datetime });
    yPositionScale.domain([maxDatetime, minDatetime])

    var minClosetime = d3.min(datapoints, function(d) { return d.Score });
    var maxClosetime = d3.max(datapoints, function(d) { return d.Score });
    xPositionScale.domain([minClosetime, maxClosetime])

    // Draw your dots
    svg.selectAll(".trumpcircles")
      .data(datapoints)
      .enter().append("circle")
        .attr("class", "trumpcircles")
        .attr("r", function(d) {
          if (d.trump != "") {return "6"}
          else {return "none"}
        })
        .attr("stroke", function(d) {
          if (d.Score > 1) {return "red"}
          else {return "none"}
        })
        .attr("stroke-width", 2)
        .attr("fill", "white")
        .attr("cx", function(d) {
          return xPositionScale(d.Score)
        })
        .attr("cy", function(d) {
          return yPositionScale(d.datetime)
        })

        .on('mouseover', function(d,i) {
          tip.show(d,i)
          svg.selectAll(".trumpcircles")
          .filter(function(e) {
            return(e.datetime != d.datetime)
          })
          .transition()
          .style("opacity", 0.2)

        })

        .on('mouseout', function(d,i) {
          tip.hide(d,i)
          svg.selectAll(".trumpcircles")
          .filter(function(e) {
            return(e.datetime != d.datetime)
          })
          .transition()
          .style("opacity", 1)
        })
    // Draw your SINGLE line (but remember it isn't called a line)
    svg.append("path")
      .datum(datapoints)
      .attr("d", line)
      .attr("fill", "none")
      .attr("stroke", "red")
      .attr("stroke-width", 3)

    // Add your axes - I've added the x axis for you
    // Notice that xAxis gets a .tickFormat to format the dates!
    // You won't use this again unless you're doing time again.
      var xAxis = d3.axisBottom(xPositionScale);
      svg.append("g")
        .attr("class", "axis x-axis")
        .call(xAxis);

      var yAxis = d3.axisLeft(yPositionScale).tickFormat(d3.timeFormat("%Y"));
      svg.append("g")
        .attr("class", "axis y-axis")
        .attr("transform", "translate(0," + height + ")")
        .call(yAxis);

    }
})();
<|endoftext|>


<commit_before>;( function() {

	"use strict";

	App.Views.Form.EntitiesSectionView = Backbone.View.extend({

		el: "#form-view #data-tab .entities-section",
		events: {
			"change .countries-select": "onCountriesSelect",
			"change [name='add-country-mode']": "onAddCountryModeChange"
		},

		initialize: function( options ) {

			this.dispatcher = options.dispatcher;
			//App.AvailableEntitiesCollection.on( "change add remove reset", this.render, this );
			//available entities are changing just on fetch so listen just for that
			App.AvailableEntitiesCollection.on( "reset fetched", this.render, this );

			this.$entitiesSelect = this.$el.find( ".countries-select" );
			this.$addCountryControlInput = this.$el.find( "[name='add-country-control']" );

			this.render();

		},

		render: function() {

			var $entitiesSelect = this.$entitiesSelect;
			$entitiesSelect.empty();

			//append default
			$entitiesSelect.append( $( "<option selected disabled>Select entity</option>" ) );

			App.AvailableEntitiesCollection.each( function( model ) {
				$entitiesSelect.append( $( "<option value='" + model.get( "id" ) + "'>" + model.get( "name" ) + "</option>" ) );
			});

			var addCountryControl = App.ChartModel.get( "add-country-control" );
			this.$addCountryControlInput.prop( "checked", addCountryControl );

		},

		onCountriesSelect: function( evt ) {

			var $select = $( evt.target ),
				val = $select.val(),
				$option = $select.find( "option[value=" + val + "]" ),
				text = $option.text();

			App.ChartModel.addSelectedCountry( { id: val, name: text } );

		},

		onAddCountryModeChange: function( evt ) {

			var $input = $( "[name='add-country-mode']:checked" );
			App.ChartModel.set( "add-country-mode", $input.val() );

		}

	});

})();<commit_msg>update correct add-country-mode in editor<commit_after>;( function() {

	"use strict";

	App.Views.Form.EntitiesSectionView = Backbone.View.extend({

		el: "#form-view #data-tab .entities-section",
		events: {
			"change .countries-select": "onCountriesSelect",
			"change [name='add-country-mode']": "onAddCountryModeChange"
		},

		initialize: function( options ) {

			this.dispatcher = options.dispatcher;
			//App.AvailableEntitiesCollection.on( "change add remove reset", this.render, this );
			//available entities are changing just on fetch so listen just for that
			App.AvailableEntitiesCollection.on( "reset fetched", this.render, this );

			this.$entitiesSelect = this.$el.find( ".countries-select" );
			this.$addCountryControlInput = this.$el.find( "[name='add-country-control']" );

			this.render();

		},

		render: function() {

			var $entitiesSelect = this.$entitiesSelect;
			$entitiesSelect.empty();

			//append default
			$entitiesSelect.append( $( "<option selected disabled>Select entity</option>" ) );

			App.AvailableEntitiesCollection.each( function( model ) {
				$entitiesSelect.append( $( "<option value='" + model.get( "id" ) + "'>" + model.get( "name" ) + "</option>" ) );
			});

			var addCountryControl = App.ChartModel.get( "add-country-control" );
			this.$addCountryControlInput.prop( "checked", addCountryControl );

			//based on stored add-country-mode
			var addCountryMode = App.ChartModel.get( "add-country-mode" );
			this.$el.find( "[name='add-country-mode']" ).filter( "[value='" + addCountryMode + "']" ).prop( "checked", true );

		},

		onCountriesSelect: function( evt ) {

			var $select = $( evt.target ),
				val = $select.val(),
				$option = $select.find( "option[value=" + val + "]" ),
				text = $option.text();

			App.ChartModel.addSelectedCountry( { id: val, name: text } );

		},

		onAddCountryModeChange: function( evt ) {

			var $input = $( "[name='add-country-mode']:checked" );
			App.ChartModel.set( "add-country-mode", $input.val() );

		}

	});

})();<|endoftext|>


<commit_before>const dbCmds = require('../db/commands.js')
const moment = require('moment-timezone')
const defaultConfigs = require('../../util/checkConfig.js').defaultConfigs
const log = require('../../util/logger.js')
const storage = require('../../util/storage.js')

function getArticleId (articleList, article) {
  let equalGuids = (articleList.length > 1) // default to true for most feeds
  if (equalGuids && articleList[0].guid) {
    articleList.forEach((article, index) => {
      if (index > 0 && article.guid !== articleList[index - 1].guid) equalGuids = false
    })
  }

  if ((!article.guid || equalGuids) && article.pubdate && article.pubdate.toString() !== 'Invalid Date') return article.pubdate.toString()
  if ((!article.guid || equalGuids) && article.title) return article.title
  return article.guid
}

module.exports = (data, callback) => {
  const { rssList, articleList, debugFeeds, link, shardId, config, feedData, scheduleName, runNum } = data // feedData is only defined when config.database.uri is set to a databaseless folder path
  if (!scheduleName) throw new Error('Missing schedule name for shared logic')
  const RSSLIST_LENGTH = Object.keys(rssList).length
  let sourcesCompleted = 0
  const totalArticles = articleList.length
  const dbIds = []
  const dbTitles = []
  const dbCustomComparisons = {} // Object with comparison names as key, and array as values whose function is similar to how dbIds and dbTitles work
  const dbCustomComparisonsValid = {} // Object with comparison names as key, and only boolean true values as values
  const dbCustomComparisonsToDelete = []
  const customComparisonsToUpdate = []
  const toInsert = []
  const toUpdate = {} // Article's resolved IDs (getArticleId) as key and the article as value
  const collectionId = storage.collectionId(link, shardId, scheduleName)
  const Feed = storage.models.FeedByCollectionId(collectionId)
  const feedCollectionId = feedData ? collectionId : undefined
  const feedCollection = feedData ? (feedData[feedCollectionId] || []) : undefined

  dbCmds.findAll(feedCollection || Feed)
    .then(docs => {
      for (var d = 0; d < docs.length; ++d) {
        const doc = docs[d]
        // Push the main data for built in comparisons
        dbIds.push(doc.id)
        dbTitles.push(doc.title)

        // Now deal with custom comparisons
        const docCustomComparisons = doc.customComparisons
        if (docCustomComparisons !== undefined && Object.keys(docCustomComparisons).length > 0) {
          for (var n in docCustomComparisons) { // n = customComparison's name (such as description, author, etc.)
            if (!dbCustomComparisons[n]) dbCustomComparisons[n] = []
            dbCustomComparisons[n].push(docCustomComparisons[n])
          }
        }
      }

      const checkCustomComparisons = Object.keys(dbCustomComparisons).length > 0
      for (var a = 0; a < articleList.length; ++a) {
        const article = articleList[a]
        article._id = getArticleId(articleList, article)
        if (checkCustomComparisons) {
        // Iterate over the values stored in the db, and see if the custom comparison names in the db exist in any of the articles. If they do, then it is marked valid
          for (var compName in dbCustomComparisons) {
            if (article[compName] !== undefined && (typeof article[compName] !== 'object' || article[compName] === null)) dbCustomComparisonsValid[compName] = true
          }
        }
        if (!dbIds.includes(article._id)) toInsert.push(article)
      }

      // If any invalid custom comparisons are found, delete them
      if (checkCustomComparisons) {
        for (var q in dbCustomComparisons) {
          if (!dbCustomComparisonsValid[q]) {
            dbCustomComparisonsToDelete.push(q)
            delete dbCustomComparisons[q]
          }
        }
      }
      dbCmds.bulkInsert(feedCollection || Feed, toInsert).then(() => {
        if (dbIds.length > 0) for (var rssName in rssList) processSource(rssName)
        else callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
      })
        .catch(err => {
          if (err) return callback(new Error(`Database Error: Unable to bulk insert articles for link ${link}`, err.message || err), { status: 'failed', link: link, rssList: rssList })
        })
    })
    .catch(err => {
      return callback(err, { status: 'failed', link: link, rssList: rssList })
    })

  function processSource (rssName) {
    const source = rssList[rssName]
    const channelId = source.channel
    const customComparisons = rssList[rssName].customComparisons // Array of names
    const sentTitles = []

    if (Array.isArray(customComparisons)) {
      for (var n = customComparisons.length - 1; n >= 0; --n) {
        const name = customComparisons[n]
        if (name === 'title' || name === 'guid' || name === 'pubdate') { // Forbidden custom comparisons since these are already used by the bot
          customComparisons.splice(n, 1)
          continue
        }
        if (!dbCustomComparisons[name] && !customComparisonsToUpdate.includes(name)) customComparisonsToUpdate.push(name) // Since this custom comparison wasn't found in the db, it might be uninitialized or not found in any articles (as checked previously)
      }
    }

    let processedArticles = 0
    if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Processing collection. Total article list length: ${articleList.length}`)

    const maxAge = config.feeds.cycleMaxAge
    const cutoffDay = moment().subtract(maxAge, 'days')

    const globalDateCheck = config.feeds.checkDates != null ? config.feeds.checkDates : defaultConfigs.feeds.checkDates.default
    const localDateCheck = source.checkDates
    const checkDate = typeof localDateCheck !== 'boolean' ? globalDateCheck : localDateCheck

    const globalTitleCheck = config.feeds.checkTitles != null ? config.feeds.checkTitles : defaultConfigs.feeds.checkTitles.default
    const localTitleCheck = source.checkTitles
    const checkTitle = typeof globalTitleCheck !== 'boolean' ? globalTitleCheck : localTitleCheck

    for (var a = articleList.length - 1; a >= 0; --a) { // Loop from oldest to newest so the queue that sends articleMessages work properly, sending the older ones first
      const article = articleList[a]
      if (dbIds.length === 0 && articleList.length !== 1) { // Only skip if the articleList length is !== 1, otherwise a feed with only 1 article to send since it may have been the first item added
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}) due to empty collection.`)
        seenArticle(true, article)
      } else if (dbIds.includes(article._id)) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), ID was matched.`)
        seenArticle(true, article)
      } else if (checkTitle && (dbTitles.includes(article.title) || sentTitles.includes(article.title))) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), Title was matched but not ID.`)
        seenArticle(true, article)
      } else if (checkDate && ((!article.pubdate || article.pubdate.toString() === 'Invalid Date') || (article.pubdate && article.pubdate.toString() !== 'Invalid Date' && article.pubdate < cutoffDay))) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), due to date check.`)
        seenArticle(true, article)
      } else {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Sending article (ID: ${article._id}, TITLE: ${article.title}) to queue for send`)
        if (checkTitle && article.title) sentTitles.push(article.title)
        seenArticle(false, article)
      }
    }

    function seenArticle (seen, article) {
      if (runNum === 0 && config.feeds.sendOldOnFirstCycle === false) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), config.feeds.sendOldOnFirstCycle is false`)
        return ++processedArticles === totalArticles ? finishSource() : null // Stops here if it already exists in table, AKA "seen"
      }

      // Check for extra user-specified comparisons
      if (seen) {
        if (!Array.isArray(customComparisons)) return ++processedArticles === totalArticles ? finishSource() : null // Stops here if it already exists in table, AKA "seen"
        for (var z = 0; z < customComparisons.length; ++z) {
          const comparisonName = customComparisons[z]
          const dbCustomComparisonValues = dbCustomComparisons[comparisonName]
          const articleCustomComparisonValue = article[comparisonName]
          if (!dbCustomComparisonValues || dbCustomComparisonValues.includes(articleCustomComparisonValue)) {
            if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}) due to custom comparison check for ${comparisonName}`)
            if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: (ID: ${article._id}, TITLE: ${article.title}) ${comparisonName} dbCustomComparisonValues: ${dbCustomComparisonValues ? JSON.stringify(dbCustomComparisonValues) : undefined} `)
            continue // The comparison must either be uninitialized or invalid (no such comparison exists in any articles from the request), handled by a previous function. OR it exists in the db
          }

          // Prepare it for update in the database
          if (!toUpdate[article._id]) {
            if (!article.customComparisons) article.customComparisons = {}
            article.customComparisons[comparisonName] = articleCustomComparisonValue
            toUpdate[article._id] = article
          }
          if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Sending article (ID: ${article._id}, TITLE: ${article.title}) due to custom comparison check for ${comparisonName}`)

          return seenArticle(false, article)
        }
        return ++processedArticles === totalArticles ? finishSource() : null
      }

      // For ArticleMessage to access once ScheduleManager receives this article
      article._delivery = {
        guildId: source.guildId, // Originally set in source through FeedSchedule.js
        dateSettings: source.dateSettings, // Not the actual date, but settings concerning date format/language/timezone. Originally set in source through FeedSchedule.js
        rssName,
        channelId,
        source
      }

      callback(null, { status: 'article', article: article })
      return ++processedArticles === totalArticles ? finishSource() : null
    }
  }

  function finishSource () {
    if (++sourcesCompleted === RSSLIST_LENGTH) finishFeed()
  }

  function finishFeed () {
    // Add missing custom comparisons if needed
    if (customComparisonsToUpdate.length > 0) {
      const mustUpdate = customComparisonsToUpdate.length
      for (var l = 0; l < mustUpdate; ++l) {
        const customComparisonName = customComparisonsToUpdate[l]
        for (var a = 0; a < articleList.length; ++a) {
          const article = articleList[a]
          const articleCustomComparisonValue = article[customComparisonName]
          if (articleCustomComparisonValue === undefined || (typeof articleCustomComparisonValue === 'object' && articleCustomComparisonValue !== null)) continue // typeof null returns 'object' even though it's not supposed to
          if (!toUpdate[article._id]) {
            if (!article.customComparisons) article.customComparisons = {}
            article.customComparisons[customComparisonName] = articleCustomComparisonValue
            toUpdate[article._id] = article
          } else toUpdate[article._id].customComparisons[customComparisonName] = articleCustomComparisonValue
        }
      }
    }

    // Update anything if necessary
    const toUpdateLength = Object.keys(toUpdate).length
    if (toUpdateLength === 0) return callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
    let c = 0
    for (var id in toUpdate) {
      const article = toUpdate[id]
      dbCmds.update(feedCollection || Feed, article, err => {
        if (err) log.cycle.error(`Failed to update an article entry`, err)
        if (++c === toUpdateLength) callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
      })
    }
  }
}
<commit_msg>Fix erroneous callback use<commit_after>const dbCmds = require('../db/commands.js')
const moment = require('moment-timezone')
const defaultConfigs = require('../../util/checkConfig.js').defaultConfigs
const log = require('../../util/logger.js')
const storage = require('../../util/storage.js')

function getArticleId (articleList, article) {
  let equalGuids = (articleList.length > 1) // default to true for most feeds
  if (equalGuids && articleList[0].guid) {
    articleList.forEach((article, index) => {
      if (index > 0 && article.guid !== articleList[index - 1].guid) equalGuids = false
    })
  }

  if ((!article.guid || equalGuids) && article.pubdate && article.pubdate.toString() !== 'Invalid Date') return article.pubdate.toString()
  if ((!article.guid || equalGuids) && article.title) return article.title
  return article.guid
}

module.exports = (data, callback) => {
  const { rssList, articleList, debugFeeds, link, shardId, config, feedData, scheduleName, runNum } = data // feedData is only defined when config.database.uri is set to a databaseless folder path
  if (!scheduleName) throw new Error('Missing schedule name for shared logic')
  const RSSLIST_LENGTH = Object.keys(rssList).length
  let sourcesCompleted = 0
  const totalArticles = articleList.length
  const dbIds = []
  const dbTitles = []
  const dbCustomComparisons = {} // Object with comparison names as key, and array as values whose function is similar to how dbIds and dbTitles work
  const dbCustomComparisonsValid = {} // Object with comparison names as key, and only boolean true values as values
  const dbCustomComparisonsToDelete = []
  const customComparisonsToUpdate = []
  const toInsert = []
  const toUpdate = {} // Article's resolved IDs (getArticleId) as key and the article as value
  const collectionId = storage.collectionId(link, shardId, scheduleName)
  const Feed = storage.models.FeedByCollectionId(collectionId)
  const feedCollectionId = feedData ? collectionId : undefined
  const feedCollection = feedData ? (feedData[feedCollectionId] || []) : undefined

  dbCmds.findAll(feedCollection || Feed)
    .then(docs => {
      for (var d = 0; d < docs.length; ++d) {
        const doc = docs[d]
        // Push the main data for built in comparisons
        dbIds.push(doc.id)
        dbTitles.push(doc.title)

        // Now deal with custom comparisons
        const docCustomComparisons = doc.customComparisons
        if (docCustomComparisons !== undefined && Object.keys(docCustomComparisons).length > 0) {
          for (var n in docCustomComparisons) { // n = customComparison's name (such as description, author, etc.)
            if (!dbCustomComparisons[n]) dbCustomComparisons[n] = []
            dbCustomComparisons[n].push(docCustomComparisons[n])
          }
        }
      }

      const checkCustomComparisons = Object.keys(dbCustomComparisons).length > 0
      for (var a = 0; a < articleList.length; ++a) {
        const article = articleList[a]
        article._id = getArticleId(articleList, article)
        if (checkCustomComparisons) {
        // Iterate over the values stored in the db, and see if the custom comparison names in the db exist in any of the articles. If they do, then it is marked valid
          for (var compName in dbCustomComparisons) {
            if (article[compName] !== undefined && (typeof article[compName] !== 'object' || article[compName] === null)) dbCustomComparisonsValid[compName] = true
          }
        }
        if (!dbIds.includes(article._id)) toInsert.push(article)
      }

      // If any invalid custom comparisons are found, delete them
      if (checkCustomComparisons) {
        for (var q in dbCustomComparisons) {
          if (!dbCustomComparisonsValid[q]) {
            dbCustomComparisonsToDelete.push(q)
            delete dbCustomComparisons[q]
          }
        }
      }
      dbCmds.bulkInsert(feedCollection || Feed, toInsert).then(() => {
        if (dbIds.length > 0) for (var rssName in rssList) processSource(rssName)
        else callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
      })
        .catch(err => {
          if (err) return callback(new Error(`Database Error: Unable to bulk insert articles for link ${link}`, err.message || err), { status: 'failed', link: link, rssList: rssList })
        })
    })
    .catch(err => {
      return callback(err, { status: 'failed', link: link, rssList: rssList })
    })

  function processSource (rssName) {
    const source = rssList[rssName]
    const channelId = source.channel
    const customComparisons = rssList[rssName].customComparisons // Array of names
    const sentTitles = []

    if (Array.isArray(customComparisons)) {
      for (var n = customComparisons.length - 1; n >= 0; --n) {
        const name = customComparisons[n]
        if (name === 'title' || name === 'guid' || name === 'pubdate') { // Forbidden custom comparisons since these are already used by the bot
          customComparisons.splice(n, 1)
          continue
        }
        if (!dbCustomComparisons[name] && !customComparisonsToUpdate.includes(name)) customComparisonsToUpdate.push(name) // Since this custom comparison wasn't found in the db, it might be uninitialized or not found in any articles (as checked previously)
      }
    }

    let processedArticles = 0
    if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Processing collection. Total article list length: ${articleList.length}`)

    const maxAge = config.feeds.cycleMaxAge
    const cutoffDay = moment().subtract(maxAge, 'days')

    const globalDateCheck = config.feeds.checkDates != null ? config.feeds.checkDates : defaultConfigs.feeds.checkDates.default
    const localDateCheck = source.checkDates
    const checkDate = typeof localDateCheck !== 'boolean' ? globalDateCheck : localDateCheck

    const globalTitleCheck = config.feeds.checkTitles != null ? config.feeds.checkTitles : defaultConfigs.feeds.checkTitles.default
    const localTitleCheck = source.checkTitles
    const checkTitle = typeof globalTitleCheck !== 'boolean' ? globalTitleCheck : localTitleCheck

    for (var a = articleList.length - 1; a >= 0; --a) { // Loop from oldest to newest so the queue that sends articleMessages work properly, sending the older ones first
      const article = articleList[a]
      if (dbIds.length === 0 && articleList.length !== 1) { // Only skip if the articleList length is !== 1, otherwise a feed with only 1 article to send since it may have been the first item added
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}) due to empty collection.`)
        seenArticle(true, article)
      } else if (dbIds.includes(article._id)) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), ID was matched.`)
        seenArticle(true, article)
      } else if (checkTitle && (dbTitles.includes(article.title) || sentTitles.includes(article.title))) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), Title was matched but not ID.`)
        seenArticle(true, article)
      } else if (checkDate && ((!article.pubdate || article.pubdate.toString() === 'Invalid Date') || (article.pubdate && article.pubdate.toString() !== 'Invalid Date' && article.pubdate < cutoffDay))) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), due to date check.`)
        seenArticle(true, article)
      } else {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Sending article (ID: ${article._id}, TITLE: ${article.title}) to queue for send`)
        if (checkTitle && article.title) sentTitles.push(article.title)
        seenArticle(false, article)
      }
    }

    function seenArticle (seen, article) {
      if (runNum === 0 && config.feeds.sendOldOnFirstCycle === false) {
        if (debugFeeds && debugFeeds.includes(rssName)) log.debug.warning(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}), config.feeds.sendOldOnFirstCycle is false`)
        return ++processedArticles === totalArticles ? finishSource() : null // Stops here if it already exists in table, AKA "seen"
      }

      // Check for extra user-specified comparisons
      if (seen) {
        if (!Array.isArray(customComparisons)) return ++processedArticles === totalArticles ? finishSource() : null // Stops here if it already exists in table, AKA "seen"
        for (var z = 0; z < customComparisons.length; ++z) {
          const comparisonName = customComparisons[z]
          const dbCustomComparisonValues = dbCustomComparisons[comparisonName]
          const articleCustomComparisonValue = article[comparisonName]
          if (!dbCustomComparisonValues || dbCustomComparisonValues.includes(articleCustomComparisonValue)) {
            if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Not sending article (ID: ${article._id}, TITLE: ${article.title}) due to custom comparison check for ${comparisonName}`)
            if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: (ID: ${article._id}, TITLE: ${article.title}) ${comparisonName} dbCustomComparisonValues: ${dbCustomComparisonValues ? JSON.stringify(dbCustomComparisonValues) : undefined} `)
            continue // The comparison must either be uninitialized or invalid (no such comparison exists in any articles from the request), handled by a previous function. OR it exists in the db
          }

          // Prepare it for update in the database
          if (!toUpdate[article._id]) {
            if (!article.customComparisons) article.customComparisons = {}
            article.customComparisons[comparisonName] = articleCustomComparisonValue
            toUpdate[article._id] = article
          }
          if (debugFeeds && debugFeeds.includes(rssName)) log.debug.info(`${rssName}: Sending article (ID: ${article._id}, TITLE: ${article.title}) due to custom comparison check for ${comparisonName}`)

          return seenArticle(false, article)
        }
        return ++processedArticles === totalArticles ? finishSource() : null
      }

      // For ArticleMessage to access once ScheduleManager receives this article
      article._delivery = {
        guildId: source.guildId, // Originally set in source through FeedSchedule.js
        dateSettings: source.dateSettings, // Not the actual date, but settings concerning date format/language/timezone. Originally set in source through FeedSchedule.js
        rssName,
        channelId,
        source
      }

      callback(null, { status: 'article', article: article })
      return ++processedArticles === totalArticles ? finishSource() : null
    }
  }

  function finishSource () {
    if (++sourcesCompleted === RSSLIST_LENGTH) finishFeed()
  }

  function finishFeed () {
    // Add missing custom comparisons if needed
    if (customComparisonsToUpdate.length > 0) {
      const mustUpdate = customComparisonsToUpdate.length
      for (var l = 0; l < mustUpdate; ++l) {
        const customComparisonName = customComparisonsToUpdate[l]
        for (var a = 0; a < articleList.length; ++a) {
          const article = articleList[a]
          const articleCustomComparisonValue = article[customComparisonName]
          if (articleCustomComparisonValue === undefined || (typeof articleCustomComparisonValue === 'object' && articleCustomComparisonValue !== null)) continue // typeof null returns 'object' even though it's not supposed to
          if (!toUpdate[article._id]) {
            if (!article.customComparisons) article.customComparisons = {}
            article.customComparisons[customComparisonName] = articleCustomComparisonValue
            toUpdate[article._id] = article
          } else toUpdate[article._id].customComparisons[customComparisonName] = articleCustomComparisonValue
        }
      }
    }

    // Update anything if necessary
    const toUpdateLength = Object.keys(toUpdate).length
    if (toUpdateLength === 0) return callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
    let c = 0
    for (var id in toUpdate) {
      const article = toUpdate[id]
      dbCmds.update(feedCollection || Feed, article).then(() => {
        if (++c >= toUpdateLength) callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
      }).catch(err => {
        log.cycle.error(`Failed to update an article entry`, err)
        if (++c >= toUpdateLength) callback(null, { status: 'success', link: link, feedCollection: feedCollection, feedCollectionId: feedCollectionId })
      })
    }
  }
}
<|endoftext|>


<commit_before>import React from "react";
import { capitalize, map } from "lodash-es";
import { connect } from "react-redux";
import { Row, Col, Panel, ListGroup } from "react-bootstrap";

import { getSoftwareUpdates, showInstallModal } from "../actions";
import { updateSetting } from "../../settings/actions";
import { Button, Flex, FlexItem, Icon, Radio, LoadingPlaceholder } from "../../base";
import Release from "./Release";
import InstallModal from "./Install";

class ChannelButton extends React.Component {

    handleClick = () => {
        this.props.onClick(this.props.channel);
    };

    render () {

        const { channel, checked } = this.props;

        return (
            <Radio
                label={`${capitalize(channel)}${channel === "stable" ? " (recommended)" : ""}`}
                checked={checked}
                onClick={this.handleClick}
            />
        );
    }
}

class SoftwareUpdateViewer extends React.Component {

    constructor (props) {
        super(props);
    }

    componentWillMount () {
        this.props.onGet();
    }

    render () {

        if (this.props.updates === null) {
            return <LoadingPlaceholder />;
        }

        const releases = this.props.updates.releases;

        let installModal;
        let updateComponent;

        if (releases.length) {
            const releaseComponents = map(releases, release =>
                <Release key={release.name} {...release} />
            );

            installModal = <InstallModal releases={releases} />;

            updateComponent = (
                <Panel>
                    <Panel.Body>
                        <Flex alignItems="center" style={{marginBottom: "15px"}}>
                            <FlexItem grow={1} shrink={0}>

                                    <Icon name="info" /> Update{releases.length === 1 ? "" : "s"} Available

                            </FlexItem>
                            <FlexItem grow={0} shrink={0} pad={15}>
                                <Button icon="download" bsStyle="primary" onClick={this.props.onShowModal}>
                                    Install
                                </Button>
                            </FlexItem>
                        </Flex>

                        <ListGroup>
                            {releaseComponents}
                        </ListGroup>
                    </Panel.Body>
                </Panel>
            );
        } else {
            updateComponent = (
                <Panel>
                    <Panel.Body>
                        <Icon bsStyle="success" name="checkmark" />
                         Software is up-to-date
                    </Panel.Body>
                </Panel>
            );
        }

        const radioComponents = map(["stable", "beta", "alpha"], channel =>
            <ChannelButton
                key={channel}
                channel={channel}
                checked={channel === this.props.channel}
                onClick={this.props.onSetSoftwareChannel}
            />
        );

        return (

                <Row>
                    <Col xs={12}>

                            Software Updates

                    </Col>
                    <Col xs={12} md={8}>
                        {updateComponent}
                    </Col>
                    <Col xs={12} md={4}>
                        <Panel>
                            <Panel.Body>
                                <Row>
                                    <Col xs={12}>
                                        Software Channel
                                        {radioComponents}
                                    </Col>
                                </Row>
                            </Panel.Body>
                        </Panel>
                    </Col>
                </Row>
                {installModal}

        );
    }
}

const mapStateToProps = (state) => ({
    updates: state.updates.software,
    channel: state.settings.data.software_channel
});

const mapDispatchToProps = (dispatch) => ({

    onGet: () => {
        dispatch(getSoftwareUpdates());
    },

    onSetSoftwareChannel: (value) => {
        dispatch(updateSetting("software_channel", value));
    },

    onShowModal: () => {
        dispatch(showInstallModal());
    }

});

export default connect(mapStateToProps, mapDispatchToProps)(SoftwareUpdateViewer);
<commit_msg>Break down updates viewer into smaller components<commit_after>import React from "react";
import { capitalize, map } from "lodash-es";
import { connect } from "react-redux";
import { Row, Col, Panel, ListGroup } from "react-bootstrap";

import { getSoftwareUpdates, showInstallModal } from "../actions";
import { updateSetting } from "../../settings/actions";
import { Button, Flex, FlexItem, Icon, Radio, LoadingPlaceholder } from "../../base";
import Release from "./Release";
import InstallModal from "./Install";

class ChannelButton extends React.Component {

    handleClick = () => {
        this.props.onClick(this.props.channel);
    };

    render () {

        const { channel, checked } = this.props;

        return (
            <Radio
                label={`${capitalize(channel)}${channel === "stable" ? " (recommended)" : ""}`}
                checked={checked}
                onClick={this.handleClick}
            />
        );
    }
}

const Releases = ({ onShowModal, releases }) => {

    if (releases.length) {
        const releaseComponents = map(releases, release =>
            <Release key={release.name} {...release} />
        );

        return (
            <Panel>
                <Panel.Body>
                    <Flex alignItems="center" style={{marginBottom: "15px"}}>
                        <FlexItem grow={1} shrink={0}>

                                <Icon name="info" /> Update{releases.length === 1 ? "" : "s"} Available

                        </FlexItem>
                        <FlexItem grow={0} shrink={0} pad={15}>
                            <Button icon="download" bsStyle="primary" onClick={onShowModal}>
                                Install
                            </Button>
                        </FlexItem>
                    </Flex>

                    <ListGroup>
                        {releaseComponents}
                    </ListGroup>
                </Panel.Body>
            </Panel>
        );
    }

    return (
        <Panel>
            <Panel.Body>
                <Icon bsStyle="success" name="checkmark" />
                 Software is up-to-date
            </Panel.Body>
        </Panel>
    );
};

class SoftwareUpdateViewer extends React.Component {

    componentWillMount () {
        this.props.onGet();
    }

    render () {

        if (this.props.updates === null) {
            return <LoadingPlaceholder />;
        }

        const releases = this.props.updates.releases;

        const radioComponents = map(["stable", "beta", "alpha"], channel =>
            <ChannelButton
                key={channel}
                channel={channel}
                checked={channel === this.props.channel}
                onClick={this.props.onSetSoftwareChannel}
            />
        );

        return (

                <Row>
                    <Col xs={12}>

                            Software Updates

                    </Col>
                    <Col xs={12} md={8}>
                        <Releases
                            releases={releases}
                            onShowModal={this.props.onShowModal}
                        />
                    </Col>
                    <Col xs={12} md={4}>
                        <Panel>
                            <Panel.Body>
                                <Row>
                                    <Col xs={12}>
                                        Software Channel
                                        {radioComponents}
                                    </Col>
                                </Row>
                            </Panel.Body>
                        </Panel>
                    </Col>
                </Row>

                {releases.length ? <InstallModal releases={releases} /> : null}

        );
    }
}

const mapStateToProps = (state) => ({
    updates: state.updates.software,
    channel: state.settings.data.software_channel
});

const mapDispatchToProps = (dispatch) => ({

    onGet: () => {
        dispatch(getSoftwareUpdates());
    },

    onSetSoftwareChannel: (value) => {
        dispatch(updateSetting("software_channel", value));
    },

    onShowModal: () => {
        dispatch(showInstallModal());
    }

});

export default connect(mapStateToProps, mapDispatchToProps)(SoftwareUpdateViewer);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Link, graphql } from 'gatsby';
import Helmet from 'react-helmet';
import { ListGroup, ListGroupItem } from '@freecodecamp/react-bootstrap';

import LearnLayout from '../../components/layouts/Learn';
import FullWidthRow from '../../components/helpers/FullWidthRow';
import ButtonSpacer from '../../components/helpers/ButtonSpacer';
import { MarkdownRemark, AllChallengeNode } from '../../redux/propTypes';

import './intro.css';

const propTypes = {
  data: PropTypes.shape({
    markdownRemark: MarkdownRemark,
    allChallengeNode: AllChallengeNode
  })
};

function renderMenuItems({ edges = [] }) {
  return edges
    .map(({ node }) => node)
    .map(({ title, fields: { slug } }) => (
      <Link key={'intro-' + slug} to={slug}>
        <ListGroupItem>{title}</ListGroupItem>
      </Link>
    ));
}

function IntroductionPage({ data: { markdownRemark, allChallengeNode } }) {
  const {
    html,
    frontmatter: { block }
  } = markdownRemark;
  const firstLesson = allChallengeNode && allChallengeNode.edges[0].node;
  const firstLessonPath = firstLesson
    ? firstLesson.fields.slug
    : '/strange-place';
  return (
    <LearnLayout>
      <Helmet>
        <title>{block} | freeCodeCamp.org</title>
      </Helmet>

        <FullWidthRow>

        </FullWidthRow>
        <FullWidthRow>
          <Link
            className='btn btn-lg btn-primary btn-block'
            to={firstLessonPath}
          >
            Go to the first lesson
          </Link>
          <ButtonSpacer />
          <Link className='btn btn-lg btn-primary btn-block' to='/learn'>
            View the curriculum
          </Link>
          <ButtonSpacer />

        </FullWidthRow>
        <FullWidthRow>

Upcoming Lessons

          <ListGroup className='intro-toc'>
            {allChallengeNode ? renderMenuItems(allChallengeNode) : null}
          </ListGroup>
        </FullWidthRow>

    </LearnLayout>
  );
}

IntroductionPage.displayName = 'IntroductionPage';
IntroductionPage.propTypes = propTypes;

export default IntroductionPage;

export const query = graphql`
  query IntroPageBySlug($slug: String!, $block: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      frontmatter {
        block
      }
      html
    }
    allChallengeNode(
      filter: { block: { eq: $block } }
      sort: { fields: [superOrder, order, challengeOrder] }
    ) {
      edges {
        node {
          fields {
            slug
          }
          title
        }
      }
    }
  }
`;
<commit_msg>fix(client): add missing bootstrap container (#37905)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Link, graphql } from 'gatsby';
import Helmet from 'react-helmet';
import { Grid, ListGroup, ListGroupItem } from '@freecodecamp/react-bootstrap';

import LearnLayout from '../../components/layouts/Learn';
import FullWidthRow from '../../components/helpers/FullWidthRow';
import ButtonSpacer from '../../components/helpers/ButtonSpacer';
import { MarkdownRemark, AllChallengeNode } from '../../redux/propTypes';

import './intro.css';

const propTypes = {
  data: PropTypes.shape({
    markdownRemark: MarkdownRemark,
    allChallengeNode: AllChallengeNode
  })
};

function renderMenuItems({ edges = [] }) {
  return edges
    .map(({ node }) => node)
    .map(({ title, fields: { slug } }) => (
      <Link key={'intro-' + slug} to={slug}>
        <ListGroupItem>{title}</ListGroupItem>
      </Link>
    ));
}

function IntroductionPage({ data: { markdownRemark, allChallengeNode } }) {
  const {
    html,
    frontmatter: { block }
  } = markdownRemark;
  const firstLesson = allChallengeNode && allChallengeNode.edges[0].node;
  const firstLessonPath = firstLesson
    ? firstLesson.fields.slug
    : '/strange-place';
  return (
    <LearnLayout>
      <Helmet>
        <title>{block} | freeCodeCamp.org</title>
      </Helmet>
      <Grid className='intro-layout-container'>
        <FullWidthRow>

        </FullWidthRow>
        <FullWidthRow>
          <Link
            className='btn btn-lg btn-primary btn-block'
            to={firstLessonPath}
          >
            Go to the first lesson
          </Link>
          <ButtonSpacer />
          <Link className='btn btn-lg btn-primary btn-block' to='/learn'>
            View the curriculum
          </Link>
          <ButtonSpacer />

        </FullWidthRow>
        <FullWidthRow>

Upcoming Lessons

          <ListGroup className='intro-toc'>
            {allChallengeNode ? renderMenuItems(allChallengeNode) : null}
          </ListGroup>
        </FullWidthRow>
      </Grid>
    </LearnLayout>
  );
}

IntroductionPage.displayName = 'IntroductionPage';
IntroductionPage.propTypes = propTypes;

export default IntroductionPage;

export const query = graphql`
  query IntroPageBySlug($slug: String!, $block: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      frontmatter {
        block
      }
      html
    }
    allChallengeNode(
      filter: { block: { eq: $block } }
      sort: { fields: [superOrder, order, challengeOrder] }
    ) {
      edges {
        node {
          fields {
            slug
          }
          title
        }
      }
    }
  }
`;
<|endoftext|>


<commit_before>'use strict';

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',

    clean: {
      //all:['dist'],
      //html:['html'],
      //css:['css'],
      //js:['js'],
      //img:['img'],
      files: ['dist']
    },

    concat: {
      options: {
        //separator:';',
        banner: '<%= banner %>',
        stripBanners: true
      },
      //js: {
      //  src:['js/*.js'],
      //  dest: 'dist/<%= pkg.name %>.js'
      //},
      //css: {
      //  src:['css/*.css'],
      //  dest: 'dist/<%= pkg.name %>.css'
      //},
      dist: {
        src: ['js/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }

    },

    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'dist/<%= pkg.name %>.min.js'
      }
    },

    jshint: {
      options: {
        jshintrc: true
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      src: {
        src: ['js/**/*.js']
      }
    },

    sass: {
      dist: {
        //options: {
          //style: 'expanded'
        //},
        files: {
          'tian.css': 'tian.scss'
        }
      }
    },

    cssmin: {
      dist: {
        files: [{
          expand: true,
          cwd: 'release/css',
          src: ['css/*.css', '!*.min.css'],
          dest: 'css',
          ext: '.min.css'
        }]
      }
    },

    watch: {
      gruntfile: {
        files: '<%= jshint.gruntfile.src %>',
        tasks: ['jshint:gruntfile']
      },
      src: {
        files: '<%= jshint.src.src %>',
        tasks: ['jshint:src']
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-sass');

  // wwwssss
  grunt.registerTask('default', ['jshint', 'clean', 'concat', 'uglify']);

};
<commit_msg>'默认配置'<commit_after>'use strict';

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',

    clean: {
      //all:['dist'],
      //html:['html'],
      //css:['css'],
      //js:['js'],
      //img:['img'],
      files: ['dist']
    },

    concat: {
      options: {
        //separator:';',
        banner: '<%= banner %>',
        stripBanners: true
      },
      //js: {
      //  src:['js/*.js'],
      //  dest: 'dist/<%= pkg.name %>.js'
      //},
      //css: {
      //  src:['css/*.css'],
      //  dest: 'dist/<%= pkg.name %>.css'
      //},
      dist: {
        src: ['js/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }

    },

    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'dist/<%= pkg.name %>.min.js'
      }
    },

    jshint: {
      options: {
        jshintrc: true
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      src: {
        src: ['js/**/*.js']
      }
    },

    sass: {
      dist: {
        //options: {
          //style: 'expanded'
        //},
        files: {
          'tian.css': 'tian.scss'
        }
      }
    },

    cssmin: {
      dist: {
        files: [{
          expand: true,
          cwd: 'release/css',
          src: ['css/*.css', '!*.min.css'],
          dest: 'css',
          ext: '.min.css'
        }]
      }
    },

    watch: {
      gruntfile: {
        files: '<%= jshint.gruntfile.src %>',
        tasks: ['jshint:gruntfile']
      },
      src: {
        files: '<%= jshint.src.src %>',
        tasks: ['jshint:src']
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-sass');

  // 这是默认配置
  grunt.registerTask('default', ['jshint', 'clean', 'concat', 'uglify']);

};
<|endoftext|>


<commit_before>'use strict';

/**********************************************************************
 * Angular Application
 **********************************************************************/
var app = angular.module('app', ['ngResource', 'ngRoute'])
  .config(function($routeProvider, $locationProvider, $httpProvider) {
    //================================================
    // Check if the user is connected
    //================================================
    var checkLoggedin = function($q, $timeout, $http, $location, $rootScope){
      // Initialize a new promise
      var deferred = $q.defer();

      // Make an AJAX call to check if the user is logged in
      $http.get('/loggedin').success(function(user){
        // Authenticated
        if (user !== '0'){
          /*$timeout(deferred.resolve, 0);*/
          window.localStorage.setItem('username', user.github.username);
          deferred.resolve();
        }
        // Not Authenticated
        else {
          $rootScope.message = 'You need to log in.';
          //$timeout(function(){deferred.reject();}, 0);
          deferred.reject();
          window.location.href='/auth/github';
        }
      });

      return deferred.promise;
    };

    function checkUser($http){
      $http.get('/loggedin').success(function(user){
      if (user !== '0'){
        console.log('hello', user.github.username);
        window.localStorage.setItem('username', user.github.username);
      }
      else{
        console.log('nah');
      }
      });
    }
    //================================================

    //================================================
    // Add an interceptor for AJAX errors
    //================================================
    $httpProvider.interceptors.push(function($q, $location) {
      return {
        response: function(response) {
          // do something on success
          return response;
        },
        responseError: function(response) {
          if (response.status === 401)
          //$location.url('/login');
          window.location.href='/auth/github';
          return $q.reject(response);
        }
      };
    });
    //================================================

    //================================================
    // Define all the routes
    //================================================
    $routeProvider
      .when('/', {
        templateUrl: '/views/main.html',
        controller: 'YelpCtrl',
        resolve: {
          app: checkUser
        }
      })
      .when('/admin', {
        templateUrl: 'views/admin.html',
        controller: 'AdminCtrl',
        resolve: {
          loggedin: checkLoggedin
        }
      })
      .when('/login', {
        templateUrl: 'views/login.html'
        //controller: 'LoginCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
    //================================================

  }) // end of config()
  .run(function($rootScope, $http){
    $rootScope.message = '';

    // Logout function is available in any pages
    $rootScope.logout = function(){
      localStorage.clear();
      $rootScope.message = 'Logged out.';
      $http.post('/logout');
    };
  });

/**********************************************************************
 * Login controller
 *********************************************************************
app.controller('LoginCtrl', function($scope, $rootScope, $http, $location) {
  // This object will be filled by the form
  $scope.user = {};

  // Register the login() function
  $scope.login = function(){
    $http.post('/login', {
      username: $scope.user.username,
      password: $scope.user.password,
    })
    .success(function(user){
      // No error: authentication OK
      $rootScope.message = 'Authentication successful!';
      $location.url('/admin');
    })
    .error(function(){
      // Error: authentication failed
      $rootScope.message = 'Authentication failed.';
      $location.url('/login');
    });
  };
});*/

/**********************************************************************
 * Yelp controller
 **********************************************************************/
app.controller('YelpCtrl', function($scope, $http, $location) {
  var $search = $('#filter-search');

  $search.keypress(function(e) {
    if (e.which == 13) {
      var input = this.value;
      search(input);
      window.localStorage.setItem('prevSearch', input);
    }
  });

  if(window.localStorage.getItem('save') && window.localStorage.getItem('prevSearch')){
    console.log('this should save the search');
    search(window.localStorage.getItem('prevSearch'));
    window.localStorage.removeItem('save');
  }

  $scope.attending = function(bar) {
    var user = window.localStorage.getItem('username');
    if(user && bar.userArray.indexOf(user)==-1){
      bar.userArray.push(user);
      var data = {users: bar.userArray};

      if(!bar.userArray){
        $http.post('/app/yelp/'+bar.id, data).success(function(response){
          console.log('response: ', response);
        });
      }
      else{
        $http.put('/app/yelp/'+bar.id, data);
      }

    }
    else if(bar.userArray.indexOf(user) > -1)
      console.log('you are already attending');
    else{
      console.log('you need to log in');
      window.localStorage.setItem('save', true);
      window.location.href='/auth/github';
    }
  };

  $scope.remove = function(bar){
      var index = bar.userArray.indexOf(window.localStorage.getItem('username'));
      bar.userArray.splice(index,1);
      var data = {users: bar.userArray};
      $http.put('/app/yelp/'+bar.id, data);
  };

  $scope.alreadyGoing = function(bar){
    var user = window.localStorage.getItem('username');
    if(user && bar.userArray.indexOf(user)>-1)
      return true;
    else
      return false;
  };

  function search(location) {
    $http.get('/app/yelp/' + location).success(function(response) {
      $scope.businesses = response;
      console.log(response);
    });
  }

});

/**********************************************************************
 * Admin controller
 **********************************************************************/
app.controller('AdminCtrl', function($scope, $http) {
  // List of users got from the server
  $scope.users = [];
  // Fill the array to display it in the page
  $http.get('/users').success(function(users){
    for (var i in users)
      $scope.users.push(users[i]);
  });
});

<commit_msg>work please<commit_after>'use strict';

/**********************************************************************
 * Angular Application
 **********************************************************************/
var app = angular.module('app', ['ngResource', 'ngRoute'])
  .config(function($routeProvider, $locationProvider, $httpProvider) {
    //================================================
    // Check if the user is connected
    //================================================
    var checkLoggedin = function($q, $timeout, $http, $location, $rootScope){
      // Initialize a new promise
      var deferred = $q.defer();

      // Make an AJAX call to check if the user is logged in
      $http.get('/loggedin').success(function(user){
        // Authenticated
        if (user !== '0'){
          /*$timeout(deferred.resolve, 0);*/
          window.localStorage.setItem('username', user.github.username);
          deferred.resolve();
        }
        // Not Authenticated
        else {
          $rootScope.message = 'You need to log in.';
          //$timeout(function(){deferred.reject();}, 0);
          deferred.reject();
          window.location.href='/auth/github';
        }
      });

      return deferred.promise;
    };

    function checkUser($http){
      $http.get('/loggedin').success(function(user){
      if (user !== '0'){
        console.log('hello', user.github.username);
        window.localStorage.setItem('username', user.github.username);
      }
      else{
        console.log('nah');
      }
      });
    }
    //================================================

    //================================================
    // Add an interceptor for AJAX errors
    //================================================
    $httpProvider.interceptors.push(function($q, $location) {
      return {
        response: function(response) {
          // do something on success
          return response;
        },
        responseError: function(response) {
          if (response.status === 401)
          //$location.url('/login');
          window.location.href='/auth/github';
          return $q.reject(response);
        }
      };
    });
    //================================================

    //================================================
    // Define all the routes
    //================================================
    $routeProvider
      .when('/', {
        templateUrl: '/views/main.html',
        controller: 'YelpCtrl',
        resolve: {
          app: checkUser
        }
      })
      .when('/admin', {
        templateUrl: 'views/admin.html',
        controller: 'AdminCtrl',
        resolve: {
          loggedin: checkLoggedin
        }
      })
      .when('/login', {
        templateUrl: 'views/login.html'
        //controller: 'LoginCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
    //================================================

  }) // end of config()
  .run(function($rootScope, $http){
    $rootScope.message = '';

    // Logout function is available in any pages
    $rootScope.logout = function(){
      localStorage.clear();
      $rootScope.message = 'Logged out.';
      $http.post('/logout');
    };
  });

/**********************************************************************
 * Login controller
 *********************************************************************
app.controller('LoginCtrl', function($scope, $rootScope, $http, $location) {
  // This object will be filled by the form
  $scope.user = {};

  // Register the login() function
  $scope.login = function(){
    $http.post('/login', {
      username: $scope.user.username,
      password: $scope.user.password,
    })
    .success(function(user){
      // No error: authentication OK
      $rootScope.message = 'Authentication successful!';
      $location.url('/admin');
    })
    .error(function(){
      // Error: authentication failed
      $rootScope.message = 'Authentication failed.';
      $location.url('/login');
    });
  };
});*/

/**********************************************************************
 * Yelp controller
 **********************************************************************/
app.controller('YelpCtrl', function($scope, $http, $location) {
  var $search = $('#filter-search');

  $search.keypress(function(e) {
    if (e.which == 13) {
      var input = this.value;
      search(input);
      window.localStorage.setItem('prevSearch', input);
    }
  });

  if(window.localStorage.getItem('save') && window.localStorage.getItem('prevSearch')){
    console.log('this should save the search');
    search(window.localStorage.getItem('prevSearch'));
    window.localStorage.removeItem('save');
  }

  $scope.attending = function(bar) {
    var user = window.localStorage.getItem('username');
    if(user && bar.userArray.indexOf(user)==-1){
      bar.userArray.push(user);
      var data = {users: bar.userArray};

      if(!bar.userArray){
        $http.post('/app/yelp/'+bar.id, data).success(function(response){
          console.log('response: ', response);
        });
      }
      else{
        $http.put('/app/yelp/'+bar.id, data).success(function(){
          console.log('PUT resquest success');
        });
      }

    }
    else if(bar.userArray.indexOf(user) > -1)
      console.log('you are already attending');
    else{
      console.log('you need to log in');
      window.localStorage.setItem('save', true);
      window.location.href='/auth/github';
    }
  };

  $scope.remove = function(bar){
      var index = bar.userArray.indexOf(window.localStorage.getItem('username'));
      bar.userArray.splice(index,1);
      var data = {users: bar.userArray};
      $http.put('/app/yelp/'+bar.id, data);
  };

  $scope.alreadyGoing = function(bar){
    var user = window.localStorage.getItem('username');
    if(user && bar.userArray.indexOf(user)>-1)
      return true;
    else
      return false;
  };

  function search(location) {
    $http.get('/app/yelp/' + location).success(function(response) {
      $scope.businesses = response;
      console.log(response);
    });
  }

});

/**********************************************************************
 * Admin controller
 **********************************************************************/
app.controller('AdminCtrl', function($scope, $http) {
  // List of users got from the server
  $scope.users = [];
  // Fill the array to display it in the page
  $http.get('/users').success(function(users){
    for (var i in users)
      $scope.users.push(users[i]);
  });
});

<|endoftext|>


<commit_before>(function ($, twttr) {
  $.fn.enable = function () {
    return this.removeAttr('disabled');
  };
  $.fn.disable = function () {
    return this.attr('disabled', 'disabled');
  };

  $(document).ready(function () {
    var popoverNormalOptions = [
      'animate',
      'delayIn',
      'delayOut',
      'fallback',
      'html',
      'live',
      'offset',
      'placement',
      'trigger'
    ];
    $('[data-popover]').each(function () {
      var $this = $(this);
      var options = {
        content: 'data-content',
        title: 'data-title'
      };
      $.each(popoverNormalOptions, function (_, v) {
        options[v] = $this.attr('data-' + v);
      });
      $this.popover(options);
    });
  });

  $(document).ready(function () {
    var twipsyNormalOptions = [
      'animate',
      'delayIn',
      'delayOut',
      'fallback',
      'html',
      'live',
      'offset',
      'placement',
      'trigger'
    ];
    $('[data-twipsy]').each(function () {
      var $this = $(this);
      var options = {
        title: 'data-title'
      };
      $.each(twipsyNormalOptions, function (_, v) {
        options[v] = $this.attr('data-' + v);
      });
      $this.twipsy(options);
    });
  });

  var callTwitterApi = function (url, method, data, done, fail, always) {
    $.ajax({
      url: url,
      type: method,
      dataType: 'json',
      data: $.extend({suppress_response_codes: '1'}, data),
    })
    .done(function (data) {
      if (data.error == null)
        done(data);
      else
        fail(data);
    })
    .fail(function (_jqXHR, textStatus, errorThrown) {
      fail({
        error: 'Failed to connect to Twitter (' +
               textStatus +
               ' / ' +
               errorThrown +
               ').'
      });
    })
    .always(function (_jqXHR, _textStatus) {
      always();
    });
  };

  var sinceIdTable = {
    '/api/1/statuses/user_timeline.json': '1'
  };
  var updateTimeline = function (apiUrl, columnId, additionalTweetClass) {
    var indicateRequestingStatus = function () {
      $('#' + columnId).twipsy('show');
    };
    var restoreRequestingStatus = function () {
      $('#' + columnId).twipsy('hide');
    };
    indicateRequestingStatus();
    callTwitterApi(
      apiUrl,
      'GET',
      {
        since_id: sinceIdTable[apiUrl],
        include_entities: 't',
        count: '20'
      },
      function (data) {
        var tweetTable = {};
        $.each(data, function (_, tweet) {
          tweetTable[tweet.id_str] = tweet;
        });
        var ids = $.map(data, function (tweet) {return tweet.id_str;});
        ids.sort();
        if (1 <= ids.length)
          sinceIdTable[apiUrl] = ids[ids.length - 1];
        $.each(ids, function (_, id) {
          var tweet = tweetTable[id];
          var d = {
            screenName: tweet.user.screen_name,
            text: twttr.txt.autoLink(
              tweet.text,
              {urlEntities: tweet.entities.urls}
            ),
            postedAt: tweet.created_at,
            id: tweet.id_str
          };
          $('#' + columnId + ' .tweets').prepend(
            $(
              $('#tweet-template').html().replace(
                /{{([^{}]+)}}/g,
                function (_, key) {
                  return d[key];
                }
              )
            )
            .addClass(additionalTweetClass)
            .find('a')
              .attr('target', '_blank')
            .end()
            .fadeIn()
          );
        });
      },
      function (data) {
        // FIXME: Alert gracefully.
        alert(data.error);
      },
      function () {
        restoreRequestingStatus();
      }
    );
  };
  var updateAllTimelines = (function () {
    var timer = null;
    return function () {
      if (timer)
        clearTimeout(timer);
      timer = setTimeout(updateAllTimelines, 5 * 60 * 1000);

      updateTimeline(
        '/api/1/statuses/user_timeline.json',
        'main-column',
        'mine'
      );
      updateTimeline(
        '/api/1/statuses/mentions.json',
        'mentions-column',
        'mention'
      );
    };
  })();

  $(document).ready(function () {
    $('#columns .tweets').empty();  // Remove dummy content.

    $('#not-signed-in #the-sign-in-menu').popover('show');
    $('#not-signed-in #tweet-form :input').disable();

    if ($('#signed-in').length) {
      $('#signed-in-user').hide();
      callTwitterApi(
        '/api/1/account/verify_credentials.json',
        'GET',
        {skip_status: 't'},
        function (user) {
          var $n = $('#signed-in-user').parent();
          $n.html(
            $n.html().replace(
              /{{([^{}]+)}}/g,
              function (_, key) {
                return user[key];
              }
            )
          );
        },
        function (data) {
          // FIXME: Alert gracefully.
          alert(data.error);
        },
        function () {
          $('#signed-in-user').fadeIn();
        }
      );
    }

    var updateCharacterCount = function () {
      $('#character-count').text(140 - $(this).val().length);  // FIXME
    };
    $('#status')
      .keydown(updateCharacterCount)
      .keyup(updateCharacterCount);

    $('#tweet-form').submit(function () {
      if ($('#status').val() == '') {
        updateAllTimelines();
      } else {
        var indicateRequestingStatus = function () {
          $('#tweet-form :input').disable();
          $('#tweet-form #status').twipsy('show');
        };
        var restoreRequestingStatus = function () {
          $('#tweet-form :input').enable();
          $('#tweet-form #status').twipsy('hide');
        };

        indicateRequestingStatus();
        callTwitterApi(
          '/api/1/statuses/update.json',
          'POST',
          {
            suppress_response_codes: '1',
            status: $('#status').val()
          },
          function (data) {
            updateAllTimelines();  // To show the last posted tweet.
            $('#status').val('');
          },
          function (data) {
            // FIXME: Alert gracefully.
            alert(data.error);
          },
          function () {
            restoreRequestingStatus();
          }
        );
      }
      return false;
    });
  });
})(jQuery, twttr);

// __END__  {{{1
// vim: expandtab shiftwidth=2 softtabstop=2
// vim: foldmethod=marker
<commit_msg>Update timelines at first<commit_after>(function ($, twttr) {
  $.fn.enable = function () {
    return this.removeAttr('disabled');
  };
  $.fn.disable = function () {
    return this.attr('disabled', 'disabled');
  };

  $(document).ready(function () {
    var popoverNormalOptions = [
      'animate',
      'delayIn',
      'delayOut',
      'fallback',
      'html',
      'live',
      'offset',
      'placement',
      'trigger'
    ];
    $('[data-popover]').each(function () {
      var $this = $(this);
      var options = {
        content: 'data-content',
        title: 'data-title'
      };
      $.each(popoverNormalOptions, function (_, v) {
        options[v] = $this.attr('data-' + v);
      });
      $this.popover(options);
    });
  });

  $(document).ready(function () {
    var twipsyNormalOptions = [
      'animate',
      'delayIn',
      'delayOut',
      'fallback',
      'html',
      'live',
      'offset',
      'placement',
      'trigger'
    ];
    $('[data-twipsy]').each(function () {
      var $this = $(this);
      var options = {
        title: 'data-title'
      };
      $.each(twipsyNormalOptions, function (_, v) {
        options[v] = $this.attr('data-' + v);
      });
      $this.twipsy(options);
    });
  });

  var callTwitterApi = function (url, method, data, done, fail, always) {
    $.ajax({
      url: url,
      type: method,
      dataType: 'json',
      data: $.extend({suppress_response_codes: '1'}, data),
    })
    .done(function (data) {
      if (data.error == null)
        done(data);
      else
        fail(data);
    })
    .fail(function (_jqXHR, textStatus, errorThrown) {
      fail({
        error: 'Failed to connect to Twitter (' +
               textStatus +
               ' / ' +
               errorThrown +
               ').'
      });
    })
    .always(function (_jqXHR, _textStatus) {
      always();
    });
  };

  var sinceIdTable = {
    '/api/1/statuses/user_timeline.json': '1'
  };
  var updateTimeline = function (apiUrl, columnId, additionalTweetClass) {
    var indicateRequestingStatus = function () {
      $('#' + columnId).twipsy('show');
    };
    var restoreRequestingStatus = function () {
      $('#' + columnId).twipsy('hide');
    };
    indicateRequestingStatus();
    callTwitterApi(
      apiUrl,
      'GET',
      {
        since_id: sinceIdTable[apiUrl],
        include_entities: 't',
        count: '20'
      },
      function (data) {
        var tweetTable = {};
        $.each(data, function (_, tweet) {
          tweetTable[tweet.id_str] = tweet;
        });
        var ids = $.map(data, function (tweet) {return tweet.id_str;});
        ids.sort();
        if (1 <= ids.length)
          sinceIdTable[apiUrl] = ids[ids.length - 1];
        $.each(ids, function (_, id) {
          var tweet = tweetTable[id];
          var d = {
            screenName: tweet.user.screen_name,
            text: twttr.txt.autoLink(
              tweet.text,
              {urlEntities: tweet.entities.urls}
            ),
            postedAt: tweet.created_at,
            id: tweet.id_str
          };
          $('#' + columnId + ' .tweets').prepend(
            $(
              $('#tweet-template').html().replace(
                /{{([^{}]+)}}/g,
                function (_, key) {
                  return d[key];
                }
              )
            )
            .addClass(additionalTweetClass)
            .find('a')
              .attr('target', '_blank')
            .end()
            .fadeIn()
          );
        });
      },
      function (data) {
        // FIXME: Alert gracefully.
        alert(data.error);
      },
      function () {
        restoreRequestingStatus();
      }
    );
  };
  var updateAllTimelines = (function () {
    var timer = null;
    return function () {
      if (timer)
        clearTimeout(timer);
      timer = setTimeout(updateAllTimelines, 5 * 60 * 1000);

      updateTimeline(
        '/api/1/statuses/user_timeline.json',
        'main-column',
        'mine'
      );
      updateTimeline(
        '/api/1/statuses/mentions.json',
        'mentions-column',
        'mention'
      );
    };
  })();

  $(document).ready(function () {
    $('#columns .tweets').empty();  // Remove dummy content.

    $('#not-signed-in #the-sign-in-menu').popover('show');
    $('#not-signed-in #tweet-form :input').disable();

    if ($('#signed-in').length) {
      $('#signed-in-user').hide();
      callTwitterApi(
        '/api/1/account/verify_credentials.json',
        'GET',
        {skip_status: 't'},
        function (user) {
          var $n = $('#signed-in-user').parent();
          $n.html(
            $n.html().replace(
              /{{([^{}]+)}}/g,
              function (_, key) {
                return user[key];
              }
            )
          );
        },
        function (data) {
          // FIXME: Alert gracefully.
          alert(data.error);
        },
        function () {
          $('#signed-in-user').fadeIn();
        }
      );

      updateAllTimelines();
    }

    var updateCharacterCount = function () {
      $('#character-count').text(140 - $(this).val().length);  // FIXME
    };
    $('#status')
      .keydown(updateCharacterCount)
      .keyup(updateCharacterCount);

    $('#tweet-form').submit(function () {
      if ($('#status').val() == '') {
        updateAllTimelines();
      } else {
        var indicateRequestingStatus = function () {
          $('#tweet-form :input').disable();
          $('#tweet-form #status').twipsy('show');
        };
        var restoreRequestingStatus = function () {
          $('#tweet-form :input').enable();
          $('#tweet-form #status').twipsy('hide');
        };

        indicateRequestingStatus();
        callTwitterApi(
          '/api/1/statuses/update.json',
          'POST',
          {
            suppress_response_codes: '1',
            status: $('#status').val()
          },
          function (data) {
            updateAllTimelines();  // To show the last posted tweet.
            $('#status').val('');
          },
          function (data) {
            // FIXME: Alert gracefully.
            alert(data.error);
          },
          function () {
            restoreRequestingStatus();
          }
        );
      }
      return false;
    });
  });
})(jQuery, twttr);

// __END__  {{{1
// vim: expandtab shiftwidth=2 softtabstop=2
// vim: foldmethod=marker
<|endoftext|>


<commit_before>if(typeof define !== 'function'){
	var define = require('amdefine')(module);
}

require.config({
	paths: {
		'angular': './vendor/angular/angular.min',
		'angular-alert':'./vendor/angular-ui-bootstrap/src/alert/alert',
		'ngResource': './vendor/angular-resource/angular-resource.min',
		'ngRoute': './vendor/angular-route/angular-route.min',
		'jquery': './vendor/jquery/dist/jquery.min',
		'html5shiv':'./vendor/html5shiv/dist/html5shiv.min',
		'respondJS': './vendor/respondJS/dest/respond.min',
		'bootstrap': './vendor/bootstrap/dist/js/bootstrap.min',
		'nprogress':'./vendor/nprogress/nprogress',
		'holder':'./vendor/holderjs/holder',
		'templates':'./views'
	},

	shim: {
		'jquery':{
			'exports':'$'
		},
		'angular': {
			'exports': 'angular'
		},
		'bootstrap':['jquery'],
		'ngRoute':['angular'],
		'ngResource': ['angular'],
		'angular-alert':['angular']
	},
	waitSeconds: 15,
	urlArgs: 'bust=v0.1.0'
});

require([
	'require',
	'jquery',
	'angular',
	'bootstrap',
	'respondJS',
	'nprogress',
	'holder',
	'angular-alert',
	'html5shiv'], function(require, $, angular){
		require(['app'],function(app){
			app.initialize();
		});
});
<commit_msg>added html5shiv to requirements<commit_after>if(typeof define !== 'function'){
	var define = require('amdefine')(module);
}

require.config({
	paths: {
		'angular': './vendor/angular/angular.min',
		'angular-alert':'./vendor/angular-ui-bootstrap/src/alert/alert',
		'ngResource': './vendor/angular-resource/angular-resource.min',
		'ngRoute': './vendor/angular-route/angular-route.min',
		'jquery': './vendor/jquery/dist/jquery.min',
		'html5shiv':'./vendor/html5shiv/dist/html5shiv.min',
		'respondJS': './vendor/respondJS/dest/respond.min',
		'bootstrap': './vendor/bootstrap/dist/js/bootstrap.min',
		'nprogress':'./vendor/nprogress/nprogress',
		'holder':'./vendor/holderjs/holder',
		'templates':'./views'
	},

	shim: {
		'jquery':{
			'exports':'$'
		},
		'angular': {
			'exports': 'angular'
		},
		'bootstrap':['jquery'],
		'ngRoute':['angular'],
		'ngResource': ['angular'],
		'angular-alert':['angular']
	},
	waitSeconds: 15,
	urlArgs: 'bust=v0.1.0'
});

require([
	'require',
	'jquery',
	'angular',
	'bootstrap',
	'respondJS',
	'nprogress',
	'html5shiv',
	'holder',
	'angular-alert',
	'html5shiv'], function(require, $, angular){
		require(['app'],function(app){
			app.initialize();
		});
});
<|endoftext|>


<commit_before>$(function(){
    console.log("Document loaded");
});

$( "#userForm" ).submit(function( event ) {
    // Stop form from submitting normally
    event.preventDefault();

    user = $("input[name='user']").val()
    url = "http://localhost:8080/perfs/"+user
    fetchData(url)
    });

function fetchData(url){
    console.log("sending ajax request to /perfs");
    $.ajax({
        url: url,
        type: 'GET',
        success : function(data) {
                var platformDataSets = data;
                console.log(platformDataSets)

                for (p=0; p < platformDataSets.length; p++){
                    createGraph(platformDataSets[p].platform, platformDataSets[p].labels, platformDataSets[p].options, platformDataSets[p].datasets);
                }
        },
        error: function(data){
            console.log("Error")
        }
    });
}

function createGraph(id, aLabels, aOptions, aDataSets){
        console.log(id);
        var plot = document.getElementById("plots")
        $(".plots").append("
<canvas id="+id+" width='100' height='100' style='border:1px solid;'></canvas>
");
        //

");
        var ctx = document.getElementById(id);
        var myChart = new Chart(ctx, {
            type: 'horizontalBar',
            data: {
                labels: aLabels,
                datasets: aDataSets
            },
            options: aOptions
            });
        return myChart;
}<commit_msg>Tuning for only platform present<commit_after>$(function(){
    console.log("Document loaded");
});

$( "#userForm" ).submit(function( event ) {
    // Stop form from submitting normally
    event.preventDefault();

    user = $("input[name='user']").val()
    url = "http://localhost:8080/perfs/"+user
    fetchData(url)
    });

function fetchData(url){
    console.log("sending ajax request to /perfs");
    $.ajax({
        url: url,
        type: 'GET',
        success : function(data) {
                var platformDataSets = data;
                console.log(platformDataSets)

                for (p=0; p < platformDataSets.length; p++){
                    if (platformDataSets.length == 1)
                        createGraph(platformDataSets[p].platform, platformDataSets[p].labels, platformDataSets[p].options, platformDataSets[p].datasets,true);
                    else
                        createGraph(platformDataSets[p].platform, platformDataSets[p].labels, platformDataSets[p].options, platformDataSets[p].datasets, false);
                }
        },
        error: function(data){
            console.log("Error")
        }
    });
}

function createGraph(id, aLabels, aOptions, aDataSets, shouldShowFull){
        console.log(id);
        var plot = document.getElementById("plots")
        if (shouldShowFull){
            $(".plots").append("
<canvas id="+id+" width='100' height='100' style='border:1px solid;'></canvas>
");
        }
        else{
            $(".plots").append("
<canvas id="+id+" width='100' height='100' style='border:1px solid;'></canvas>
");
        }
        //

");
        var ctx = document.getElementById(id);
        var myChart = new Chart(ctx, {
            type: 'horizontalBar',
            data: {
                labels: aLabels,
                datasets: aDataSets
            },
            options: aOptions
            });
        return myChart;
}<|endoftext|>