<commit_before>var ASQPlugin = require('asq-plugin');
var ObjectId = require("bson-objectid");
var Promise = require('bluebird');
var coroutine = Promise.coroutine;
var cheerio = require('cheerio');
var mongoose = require('mongoose');
var ObjectId = mongoose.Types.ObjectId;
var assert = require('assert');
var _ = require('lodash');

//http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#boolean-attributes
function getBooleanValOfBooleanAttr(attrName, attrValue){
  if(attrValue === '' || attrValue === attrName){
    return true;
  }
  return false;
}

module.exports = ASQPlugin.extend({
  tagName : 'asq-java-q',

  hooks:{
    "parse_html" : "parseHtml",
    "answer_submission" : "answerSubmission",
    "presenter_connected" : "presenterConnected",
    "viewer_connected" : "viewerConnected"
  },

  parseHtml: coroutine(function* parseHtmlGen(options){
    var $ = cheerio.load(options.html,  {
      decodeEntities: false,
      lowerCaseAttributeNames:false,
      lowerCaseTags:false,
      recognizeSelfClosing: true
    });
    var mcQuestions = [];

    var all = $(this.tagName).toArray();
    for ( var i in all) {
      var el = all[i];
      var question = yield this.processEl($, el);
      mcQuestions.push(question);
    }

    options.html = $.root().html();
    //return Promise that resolves with the (maybe modified) html
    return this.asq.db.model("Question").create(mcQuestions)
    .then(function(){

      return Promise.resolve(options);
    });

  }),

  copyAnswers : function(answer, question){
    for(var i = 0, l = answer.options.length; i<l; i++){
      for(var j = 0, l2 = question.data.options.length; j<l2; j++){
        if(answer.options[i].uid.toString()  == question.data.options[j].uid){
          question.data.options[j].sum = answer.options[i].sum;
          question.data.options[j].uid = question.data.options[j].uid;
          break;
        }
      }
    }
  },

  parseSettings: coroutine(function* parseSettingsGen($, el) {
    var $el = $(el);
    var attrs = $el.attr();

    var settings = this.asq.api.settings.defaultSettings['question'];

    for ( var i in settings ) {
      if ( attrs.hasOwnProperty(settings[i].key) ) {
        // validation ?
        settings[i].value = attrs[settings[i].key];
      }
      else {
        this.writeSettingAsAttribute($el, settings[i]);
      }
    }

    // settings = yield this.createSettings(settings);
    return settings
  }),

  writeSettingAsAttribute: function($el, setting) {
    // boolean
    $el.attr(setting.key, setting.value);
  },

  restorePresenterForSession: coroutine(function *restorePresenterForSessionGen(session_id, presentation_id){
    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });

    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $unwind: "$submission" },
      { $project : {  "submitDate":1,
                      "submission._id" : "$submission._id",
                      "submission.num" : { $cond : [ "$submission.value", 1, 0 ] }
        }
      },
      { $group:{
          "_id":{
            "question" : "$_id.question",
            "option_id": "$submission._id"
          },
          "sum":{$sum: "$submission.num"},
          "submitDate":{$first: "$submitDate"},
          "submission": {$first: "$submission"},
        }
      },
      { $group:{
          "_id": {
            "question" : "$_id.question",
          },
          "options":{$push: {"uid" : "$_id.option_id" , "sum": "$sum"}}
        }
      },
      { $project : {
          "_id": 0,
          "question" : "$_id.question",
          "options" : 1
        }
      }
    ]

    var answers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();
    questions.forEach(function(q){
      q.uid = q._id.toString();
      q.data.options.forEach(function(opt){
        opt.uid = opt._id.toString();
        delete opt._id;
      });
      for(var i=0, l=answers.length; i<l; i++){
        if(answers[i].question.toString() == q._id){
          this.copyAnswers(answers[i], q);
          break;
        }
      }
    }.bind(this));

    return questions;
  }),

  countAnswers : coroutine(function *countAnswersGen(session_id, presentation_id){
    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });
    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $group: { _id: null, count: { $sum: 1 } } },
      { $project : {
          "_id": 0,
          "count" : 1
        }
      }
    ]

    var total = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    var count = 0;
    if ( total.length ) {
      count = total[0].count;
    }

    return count;
  }),

  presenterConnected: coroutine(function *presenterConnectedGen (info){

    if(! info.session_id) return info;

    var questionsWithScores = yield this.restorePresenterForSession(info.session_id, info.presentation_id);

    var total = 0;

    if(questionsWithScores.length){
      total = yield this.countAnswers(info.session_id, info.presentation_id);
    }

    var event = {
      questionType: this.tagName,
      type: 'restorePresenter',
      questions: questionsWithScores,
      total:total
    }

    this.asq.socket.emit('asq:question_type', event, info.socketId)

    //this will be the argument to the next hook
    return info;
  }),

  restoreViewerForSession: coroutine(function *restoreViewerForSessionGen(session_id, presentation_id, whitelistId){

    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });

    var pipeline = [
      { $match: {
          "session": session_id,
          "answeree" : whitelistId,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id": "$question",
          "options": {$first:"$submission"},
        }
      },
      { $project:{
          "_id": 0,
          "uid" : "$_id",
          "options": 1,
        }
      }
    ]
    var questionsWithAnswers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    questionsWithAnswers.forEach(function(q){
      q.options.forEach(function(opt){
        opt.uid = opt._id.toString();
        delete opt._id
      })
    });

    return questionsWithAnswers;
  }),

  viewerConnected: coroutine(function *viewerConnectedGen (info){

    if(! info.session_id) return info;

    var questionsWithAnswers = yield this.restoreViewerForSession(info.session_id, info.presentation_id, info.whitelistId);

    var event = {
      questionType: this.tagName,
      type: 'restoreViewer',
      questions: questionsWithAnswers
    }

    this.asq.socket.emit('asq:question_type', event, info.socketId)

    // this will be the argument to the next hook
    return info;
  }),

  answerSubmission: coroutine(function *answerSubmissionGen (answer){
    // make sure answer question exists
    var questionUid = answer.questionUid
    var question = yield this.asq.db.model("Question").findById(questionUid).exec();
    assert(question,
      'Could not find question with id' + questionUid + 'in the database');

    //make sure it's an answer for an asq-java-q question
    if(question.type !== this.tagName) {
      return answer;
    }

    // make sure options are valid
    var options = answer.options
    assert(_.isArray(options),
      'Invalid answer format, answer.submission should be an array.');

    var sanitized = [];
    var sOptionUids = options.map(function optionMap(option){
      //sanitize
      var option = _.pick(option, 'uid', 'value');
       assert(ObjectId.isValid(option.uid),
        'Invalid answer format, option should have a uid property');

      sanitized.push({_id: ObjectId(option.uid), value: option.value})

      return option.uid;
    });

    var qOptionUids = question.data.options.map(function optionMap2(option){
      return option._id.toString();
    })

    //check if the arrays have the same elements
    assert(_.isEmpty(_.xor(qOptionUids, sOptionUids)),
      'Invalid answer, submitted option uids do not match those in the database');

    answer.submission = sanitized;

    //persist
    yield this.asq.db.model("Answer").create({
      exercise   : answer.exercise_id,
      question   : questionUid,
      answeree   : answer.answeree,
      session    : answer.session,
      type       : question.type,
      submitDate : Date.now(),
      submission : answer.submission,
      confidence : answer.confidence
    });

    this.calculateProgress(answer.session, ObjectId(questionUid));

    //this will be the argument to the next hook
    return answer;
  }),

  calculateProgress: coroutine(function *calculateProgressGen(session_id, question_id){
    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : question_id
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $unwind: "$submission" },
      { $project : {  "submitDate":1,
                      "submission._id" : "$submission._id",
                      "submission.num" : { $cond : [ "$submission.value", 1, 0 ] }
        }
      },
      { $group:{
          "_id":{
            "question" : "$_id.question",
            "option_id": "$submission._id"
          },
          "sum":{$sum: "$submission.num"},
          "submitDate":{$first: "$submitDate"},
          "submission": {$first: "$submission"},
        }
      },
      { $group:{
          "_id": {
            "question" : "$_id.question",
          },
          "options":{$push: {"uid" : "$_id.option_id" , "sum": "$sum"}}
        }
      },
      { $project : {
          "_id": 0,
          "question" : "$_id.question",
          "options" : 1
        }
      }
    ]

    var answers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    var pipeline2 = [
      { $match: {
          "session": session_id,
          "question" : question_id
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $group: { _id: null, count: { $sum: 1 } } },
      { $project : {
          "_id": 0,
          "count" : 1
        }
      }
    ]

    var total = yield this.asq.db.model('Answer').aggregate(pipeline2).exec();

    var count = 0;
    if ( total.length ) {
      total = total[0].count;
    }

    var event = {
      questionType: this.tagName,
      type: 'progress',
      question: answers[0],
      total: total
    }

    this.asq.socket.emitToRoles('asq:question_type', event, session_id.toString(), 'ctrl')
  }),

  processEl: coroutine(function* processElGen($, el){

    var $el = $(el);

    //make sure question has a unique id
    var uid = $el.attr('uid');
    if(uid == undefined || uid.trim() == ''){
      $el.attr('uid', uid = ObjectId().toString() );
    }

    //get stem
    var stem = $el.find('asq-stem');
    if(stem.length){
      stem = stem.eq(0).html();
    }else{
      stem = '';
    }

    //parse options
    var options = this.parseOptions($, el);

    var settings = yield this.parseSettings($, el);

    return {
      _id : uid,
      type: this.tagName,
      data: {
        html: $.html($el),
        stem: stem,
        options: options
      },
      settings: settings
    }

  }),

  parseOptions: function($, el){

    var dbOptions = [];
    var ids = Object.create(null);
    var $el = $(el);

    var $asqOptions = $el.find('asq-option');
    assert($asqOptions.length > 1
      , 'A multi-choice question should have at least two `asq-option` children' )

    $asqOptions.each(function(idx, option){
      $option = $(option);

      //make sure optiosn are id'ed
      var uid = $option.attr('uid');
      if(uid == undefined || uid.trim() == ''){
        $option.attr('uid', uid = ObjectId().toString() );
      }

      assert(!ids[uid]
        , 'A multi-choice question cannot have two `asq-option` elements with the same uids' );

      ids[uid] = true;

      //check if the options is marked as a correct choice
      var correct = getBooleanValOfBooleanAttr("correct", $option.attr('correct'));

      //remove correct Attr so that it doesn't get served in HTML
      $option.removeAttr('correct');
      dbOptions.push({
        _id : ObjectId(uid),
        html: $option.html(),
        correct : correct
      });
    });

    return dbOptions;
  }
});
<commit_msg>dummy commit<commit_after>var ASQPlugin = require('asq-plugin');
var ObjectId = require("bson-objectid");
var Promise = require('bluebird');
var coroutine = Promise.coroutine;
var cheerio = require('cheerio');
var mongoose = require('mongoose');
var ObjectId = mongoose.Types.ObjectId;
var assert = require('assert');
var _ = require('lodash');

//http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#boolean-attributes
function getBooleanValOfBooleanAttr(attrName, attrValue){
  if(attrValue === '' || attrValue === attrName){
    return true;
  }
  return false;
}

module.exports = ASQPlugin.extend({
  tagName : 'asq-java-q',

  hooks:{
    "parse_html" : "parseHtml",
    "answer_submission" : "answerSubmission",
    "presenter_connected" : "presenterConnected",
    "viewer_connected" : "viewerConnected"
  },

  parseHtml: coroutine(function* parseHtmlGen(options){
    var $ = cheerio.load(options.html,  {
      decodeEntities: false,
      lowerCaseAttributeNames:false,
      lowerCaseTags:false,
      recognizeSelfClosing: true
    });
    var mcQuestions = [];

    var all = $(this.tagName).toArray();
    for ( var i in all) {
      var el = all[i];
      var question = yield this.processEl($, el);
      mcQuestions.push(question);
    }

    options.html = $.root().html();
    //return Promise that resolves with the (maybe modified) html
    return this.asq.db.model("Question").create(mcQuestions)
    .then(function(){

      return Promise.resolve(options);
    });

  }),

  copyAnswers : function(answer, question){
    for(var i = 0, l = answer.options.length; i<l; i++){
      for(var j = 0, l2 = question.data.options.length; j<l2; j++){
        if(answer.options[i].uid.toString()  == question.data.options[j].uid){
          question.data.options[j].sum = answer.options[i].sum;
          question.data.options[j].uid = question.data.options[j].uid;
          break;
        }
      }
    }
  },

  parseSettings: coroutine(function* parseSettingsGen($, el) {
    var $el = $(el);
    var attrs = $el.attr();

    var settings = this.asq.api.settings.defaultSettings['question'];

    for ( var i in settings ) {
      if ( attrs.hasOwnProperty(settings[i].key) ) {
        // validation ?
        settings[i].value = attrs[settings[i].key];
      }
      else {
        this.writeSettingAsAttribute($el, settings[i]);
      }
    }

    // settings = yield this.createSettings(settings);
    return settings
  }),

  writeSettingAsAttribute: function($el, setting) {
    // boolean
    $el.attr(setting.key, setting.value);
  },

  restorePresenterForSession: coroutine(function *restorePresenterForSessionGen(session_id, presentation_id){
    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });

    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $unwind: "$submission" },
      { $project : {  "submitDate":1,
                      "submission._id" : "$submission._id",
                      "submission.num" : { $cond : [ "$submission.value", 1, 0 ] }
        }
      },
      { $group:{
          "_id":{
            "question" : "$_id.question",
            "option_id": "$submission._id"
          },
          "sum":{$sum: "$submission.num"},
          "submitDate":{$first: "$submitDate"},
          "submission": {$first: "$submission"},
        }
      },
      { $group:{
          "_id": {
            "question" : "$_id.question",
          },
          "options":{$push: {"uid" : "$_id.option_id" , "sum": "$sum"}}
        }
      },
      { $project : {
          "_id": 0,
          "question" : "$_id.question",
          "options" : 1
        }
      }
    ]

    var answers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();
    questions.forEach(function(q){
      q.uid = q._id.toString();
      q.data.options.forEach(function(opt){
        opt.uid = opt._id.toString();
        delete opt._id;
      });
      for(var i=0, l=answers.length; i<l; i++){
        if(answers[i].question.toString() == q._id){
          this.copyAnswers(answers[i], q);
          break;
        }
      }
    }.bind(this));

    return questions;
  }),

  countAnswers : coroutine(function *countAnswersGen(session_id, presentation_id){
    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });
    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $group: { _id: null, count: { $sum: 1 } } },
      { $project : {
          "_id": 0,
          "count" : 1
        }
      }
    ]

    var total = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    var count = 0;
    if ( total.length ) {
      count = total[0].count;
    }

    return count;
  }),

  presenterConnected: coroutine(function *presenterConnectedGen (info){

    if(! info.session_id) return info;

    var questionsWithScores = yield this.restorePresenterForSession(info.session_id, info.presentation_id);

    var total = 0;

    if(questionsWithScores.length){
      total = yield this.countAnswers(info.session_id, info.presentation_id);
    }

    var event = {
      questionType: this.tagName,
      type: 'restorePresenter',
      questions: questionsWithScores,
      total:total
    }

    this.asq.socket.emit('asq:question_type', event, info.socketId)

    //this will be the argument to the next hook
    return info;
  }),

  restoreViewerForSession: coroutine(function *restoreViewerForSessionGen(session_id, presentation_id, whitelistId){

    var questions = yield this.asq.db.getPresentationQuestionsByType(presentation_id, this.tagName);
    var questionIds = questions.map(function(q){
      return q._id;
    });

    var pipeline = [
      { $match: {
          "session": session_id,
          "answeree" : whitelistId,
          "question" : {$in : questionIds}
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id": "$question",
          "options": {$first:"$submission"},
        }
      },
      { $project:{
          "_id": 0,
          "uid" : "$_id",
          "options": 1,
        }
      }
    ]
    var questionsWithAnswers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    questionsWithAnswers.forEach(function(q){
      q.options.forEach(function(opt){
        opt.uid = opt._id.toString();
        delete opt._id
      })
    });

    return questionsWithAnswers;
  }),

  viewerConnected: coroutine(function *viewerConnectedGen (info){

    if(! info.session_id) return info;

    var questionsWithAnswers = yield this.restoreViewerForSession(info.session_id, info.presentation_id, info.whitelistId);

    var event = {
      questionType: this.tagName,
      type: 'restoreViewer',
      questions: questionsWithAnswers
    }

    this.asq.socket.emit('asq:question_type', event, info.socketId)

    // this will be the argument to the next hook
    return info;
  }),

  answerSubmission: coroutine(function *answerSubmissionGen (answer){
    // make sure answer question exists
    var questionUid = answer.questionUid
    var question = yield this.asq.db.model("Question").findById(questionUid).exec();
    assert(question,
      'Could not find question with id' + questionUid + 'in the database');

    //make sure it's an answer for an asq-java-q question
    if(question.type !== this.tagName) {
      return answer;
    }

    // make sure options are valid
    var options = answer.options
    assert(_.isArray(options),
      'Invalid answer format, answer.submission should be an array.');

    var sanitized = [];
    var sOptionUids = options.map(function optionMap(option){
      //sanitize
      var option = _.pick(option, 'uid', 'value');
       assert(ObjectId.isValid(option.uid),
        'Invalid answer format, option should have a uid property');

      sanitized.push({_id: ObjectId(option.uid), value: option.value})

      return option.uid;
    });

    var qOptionUids = question.data.options.map(function optionMap2(option){
      return option._id.toString();
    })

    //check if the arrays have the same elements
    assert(_.isEmpty(_.xor(qOptionUids, sOptionUids)),
      'Invalid answer, submitted option uids do not match those in the database');

    answer.submission = sanitized;

    //persist
    yield this.asq.db.model("Answer").create({
      exercise   : answer.exercise_id,
      question   : questionUid,
      answeree   : answer.answeree,
      session    : answer.session,
      type       : question.type,
      submitDate : Date.now(),
      submission : answer.submission,
      confidence : answer.confidence
    });

    this.calculateProgress(answer.session, ObjectId(questionUid));

    //this will be the argument to the next hook
    return answer;
  }),

  calculateProgress: coroutine(function *calculateProgressGen(session_id, question_id){
    var pipeline = [
      { $match: {
          "session": session_id,
          "question" : question_id
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $unwind: "$submission" },
      { $project : {  "submitDate":1,
                      "submission._id" : "$submission._id",
                      "submission.num" : { $cond : [ "$submission.value", 1, 0 ] }
        }
      },
      { $group:{
          "_id":{
            "question" : "$_id.question",
            "option_id": "$submission._id"
          },
          "sum":{$sum: "$submission.num"},
          "submitDate":{$first: "$submitDate"},
          "submission": {$first: "$submission"},
        }
      },
      { $group:{
          "_id": {
            "question" : "$_id.question",
          },
          "options":{$push: {"uid" : "$_id.option_id" , "sum": "$sum"}}
        }
      },
      { $project : {
          "_id": 0,
          "question" : "$_id.question",
          "options" : 1
        }
      }
    ]

    var answers = yield this.asq.db.model('Answer').aggregate(pipeline).exec();

    var pipeline2 = [
      { $match: {
          "session": session_id,
          "question" : question_id
        }
      },
      { $sort:{"submitDate": -1}},
      { $group:{
          "_id":{
            "answeree" : "$answeree",
            "question" : "$question"
          },
          "submitDate":{$first:"$submitDate"},
          "submission": {$first:"$submission"},
        }
      },
      { $group: { _id: null, count: { $sum: 1 } } },
      { $project : {
          "_id": 0,
          "count" : 1
        }
      }
    ]

    var total = yield this.asq.db.model('Answer').aggregate(pipeline2).exec();

    var count = 0;
    if ( total.length ) {
      total = total[0].count;
    }

    var event = {
      questionType: this.tagName,
      type: 'progress',
      question: answers[0],
      total: total
    }

    this.asq.socket.emitToRoles('asq:question_type', event, session_id.toString(), 'ctrl')
  }),

  processEl: coroutine(function* processElGen($, el){

    var $el = $(el);

    //make sure question has a unique id
    var uid = $el.attr('uid');
    if(uid == undefined || uid.trim() == ''){
      $el.attr('uid', uid = ObjectId().toString() );
    }

    //get stem
    var stem = $el.find('asq-stem');
    if(stem.length){
      stem = stem.eq(0).html();
    }else{
      stem = '';
    }

    //parse options
    var options = this.parseOptions($, el);

    var settings = yield this.parseSettings($, el);

    return {
      _id : uid,
      type: this.tagName,
      data: {
        html: $.html($el),
        stem: stem,
      },
      settings: settings
    }

  }),

  parseOptions: function($, el){

    var dbOptions = [];
    var ids = Object.create(null);
    var $el = $(el);

    var $asqOptions = $el.find('asq-option');
    assert($asqOptions.length > 1
      , 'A multi-choice question should have at least two `asq-option` children' )

    $asqOptions.each(function(idx, option){
      $option = $(option);

      //make sure optiosn are id'ed
      var uid = $option.attr('uid');
      if(uid == undefined || uid.trim() == ''){
        $option.attr('uid', uid = ObjectId().toString() );
      }

      assert(!ids[uid]
        , 'A multi-choice question cannot have two `asq-option` elements with the same uids' );

      ids[uid] = true;

      //check if the options is marked as a correct choice
      var correct = getBooleanValOfBooleanAttr("correct", $option.attr('correct'));

      //remove correct Attr so that it doesn't get served in HTML
      $option.removeAttr('correct');
      dbOptions.push({
        _id : ObjectId(uid),
        html: $option.html(),
        correct : correct
      });
    });

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


<commit_before>var util = require('util'),
    _ = require('underscore'),
    jsdom = require('jsdom'),
    domtohtml = require('jsdom/lib/jsdom/browser/domtohtml'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    relations = require('../relations'),
    Asset = require('./Asset'),
    Text = require('./Text');

function Html(config) {
    Text.call(this, config);
}

util.inherits(Html, Text);

function extractEncodingFromText(text) {
    var metaCharset;
    (text.match(/<meta[^>]+>/ig) || []).forEach(function (metaTagString) {
        if (/\bhttp-equiv=([\"\']|)\s*Content-Type\s*\1/i.test(metaTagString)) {
            var matchContent = metaTagString.match(/\bcontent=([\"\']|)\s*text\/html;\s*charset=([\w\-]*)\s*\1/i);
            if (matchContent) {
                metaCharset = matchContent[2];
            }
        }
    });
    return metaCharset; // Will be undefined if not found
}

function isRelationUrl(url) {
    return url && !/^javascript:|^mailto:|^\s*$|^#/i.test(url);
}

extendWithGettersAndSetters(Html.prototype, {
    contentType: 'text/html',

    defaultExtension: '.html',

    alternativeExtensions: ['.template', '.php', '.xhtml', 'shtml'],

    isPretty: false,

    get encoding() {
        if (!this._encoding) {
            // An explicit encoding (Content-Type header, data: url charset, assetConfig) takes precedence, but if absent we should
            // look for a <meta http-equiv='Content-Type' ...> tag with a charset before falling back to the defaultEncoding (utf-8)
            if ('_text' in this) {
                this._encoding = extractEncodingFromText(this._text) || this.defaultEncoding;
            } else if (this._rawSrc) {
                this._encoding = extractEncodingFromText(this._rawSrc.toString('binary', 0, Math.min(1024, this._rawSrc.length))) || this.defaultEncoding;
            } else {
                this._encoding = this.defaultEncoding;
            }
        }
        return this._encoding;
    },

    set encoding(encoding) {
        // An intended side effect of getting this.parseTree before deleting this._rawSrc is that we're sure
        // that the raw source has been decoded into this._text before the original encoding is thrown away.
        var parseTree = this.parseTree;
        if (parseTree.head) {
            var existingMetaElements = parseTree.head.getElementsByTagName('meta'),
                contentTypeMetaElement;
            for (var i = 0 ; i < existingMetaElements.length ; i += 1) {
                var metaElement = existingMetaElements[i];
                if (/^content-type$/i.test(metaElement.getAttribute('http-equiv'))) {
                    contentTypeMetaElement = metaElement;
                    break;
                }
            }
            if (!contentTypeMetaElement) {
                contentTypeMetaElement = parseTree.createElement('meta');
                contentTypeMetaElement.setAttribute('http-equiv', 'Content-Type');
                parseTree.head.insertBefore(contentTypeMetaElement, parseTree.head.firstChild);
                this.markDirty();
            }
            if ((contentTypeMetaElement.getAttribute('content') || '').toLowerCase() !== 'text/html; charset=' + encoding) {
                contentTypeMetaElement.setAttribute('content', 'text/html; charset=' + encoding);
                this.markDirty();
            }
        }
        if (encoding !== this.encoding) {
            this._encoding = encoding;
            this.markDirty();
        }
    },

    get text() {
        if (!('_text' in this)) {
            if (this._parseTree) {
                this._text = (this._parseTree.doctype ? this._parseTree.doctype + "\n" : "") + domtohtml.domToHtml(this._parseTree, !this.isPretty);
            } else {
                this._text = this._getTextFromRawSrc();
            }
        }
        return this._text;
    },

    get parseTree() {
        if (!this._parseTree) {
            try {
                this._parseTree = jsdom.jsdom(this.text, undefined, {features: {ProcessExternalResources: [], FetchExternalResources: []}});
            } catch (e) {
                throw new Error('Parse error in ' + (this.url || 'inline Html' + (this.nonInlineAncestor ? ' in ' + this.nonInlineAncestor.url : '')) + '\n' + e.message);
            }
            // Jsdom (or its Html parser) doesn't strip the newline after the <!DOCTYPE> for some reason.
            // Issue reported here: https://github.com/tmpvar/jsdom/issues/160
            if (this._parseTree.firstChild && this._parseTree.firstChild.nodeName === '#text' && this._parseTree.firstChild.nodeValue === "\n") {
                this._parseTree.removeChild(this._parseTree.firstChild);
            }
        }
        return this._parseTree;
    },

    set parseTree(parseTree) {
        this._parseTree = parseTree;
        delete this._rawSrc;
        delete this._text;
        this.markDirty();
    },

    findOutgoingRelationsInParseTree: function () {
        var outgoingRelations = [];
        if (this.parseTree.documentElement && this.parseTree.documentElement.hasAttribute('manifest')) {
            outgoingRelations.push(new relations.HtmlCacheManifest({
                from: this,
                to: {
                    url: this.parseTree.documentElement.getAttribute('manifest')
                },
                node: this.parseTree.documentElement
            }));
        }
        var queue = [this.parseTree];
        while (queue.length) {
            var node = queue.shift();
            if (node.childNodes) {
                for (var i = node.childNodes.length - 1 ; i >= 0 ; i -= 1) {
                    queue.unshift(node.childNodes[i]);
                }
            }
            if (node.nodeType === node.ELEMENT_NODE) {
                var nodeName = node.nodeName.toLowerCase();
                if (nodeName === 'script') {
                    if (node.hasAttribute('src')) {
                        var src = node.getAttribute('src');
                        if (isRelationUrl(src)) {
                            outgoingRelations.push(new relations.HtmlScript({
                                from: this,
                                to: {
                                    url: src
                                },
                                node: node
                            }));
                            if (/(^|\/)require\.js$/.test(src) && node.hasAttribute('data-main')) {
                                outgoingRelations.push(new relations.HtmlRequireJsMain({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('data-main') + '.js'
                                    },
                                    node: node
                                }));
                            }
                        }
                    } else {
                        outgoingRelations.push(new relations.HtmlScript({
                            from: this,
                            to: new (require(node.getAttribute('type') === 'text/coffeescript' ? './CoffeeScript' : './JavaScript'))({
                                text: node.firstChild ? node.firstChild.nodeValue : ''
                            }),
                            node: node
                        }));
                    }
                } else if (nodeName === 'style') {
                    outgoingRelations.push(new relations.HtmlStyle({
                        from: this,
                        to: new (require('./Css'))({
                            text: node.firstChild ? node.firstChild.nodeValue : ''
                        }),
                        node: node
                    }));
                } else if (nodeName === 'link') {
                    if (node.hasAttribute('rel')) {
                        var rel = node.getAttribute('rel');
                        if (/(?:^| )stylesheet(?:\/less)?(?:$| )/i.test(rel) && isRelationUrl(node.getAttribute('href'))) {
                            outgoingRelations.push(new relations.HtmlStyle({
                                from: this,
                                to: {
                                    url: node.getAttribute('href')
                                },
                                node: node
                            }));
                        } else if (/(?:^| )(?:apple-touch-icon(?:-precomposed)?|icon)(?:$| )/i.test(rel)) { // Also catches rel="shortcut icon"
                            if (isRelationUrl(node.getAttribute('href'))) {
                                outgoingRelations.push(new relations.HtmlShortcutIcon({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('href')
                                    },
                                    node: node
                                }));
                            }
                        } else if (/(?:^| )apple-touch-startup-image(?:$| )/i.test(rel)) {
                            if (isRelationUrl(node.getAttribute('href'))) {
                                outgoingRelations.push(new relations.HtmlAppleTouchStartupImage({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('href')
                                    },
                                    node: node
                                }));
                            }
                        } else if (/(?:^| )alternate(?:$| )/i.test(rel)) {
                            if (isRelationUrl(node.getAttribute('href'))) {
                                var assetConfig = {
                                    url: node.getAttribute('href')
                                };
                                if (node.hasAttribute('type')) {
                                    assetConfig.contentType = node.getAttribute('type');
                                }
                                outgoingRelations.push(new relations.HtmlAlternateLink({
                                    from: this,
                                    to: assetConfig,
                                    node: node
                                }));
                            }
                        }
                    }
                } else if (nodeName === 'img') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlImage({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'a') {
                    var href = node.getAttribute('href');
                    if (isRelationUrl(href)) {
                        outgoingRelations.push(new relations.HtmlAnchor({
                            from: this,
                            to: {
                                url: href
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'iframe') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlIFrame({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'frame') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlFrame({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'esi:include') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlEdgeSideInclude({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'video') {
                    if (node.hasAttribute('src')) {
                        if (isRelationUrl(node.getAttribute('src'))) {
                            outgoingRelations.push(new relations[nodeName === 'video' ? 'HtmlVideo' : 'HtmlAudio']({
                                from: this,
                                to: {
                                    url: node.getAttribute('src')
                                },
                                node: node
                            }));
                        }
                    }
                    if (node.hasAttribute('poster') && isRelationUrl(node.getAttribute('poster'))) {
                        outgoingRelations.push(new relations.HtmlVideoPoster({
                            from: this,
                            to: {
                                url: node.getAttribute('poster')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'audio') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlAudio({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (/^(?:source|track)$/.test(nodeName) && node.parentNode && /^(?:video|audio)$/i.test(node.parentNode.nodeName)) {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations[node.parentNode.nodeName.toLowerCase() === 'video' ? 'HtmlVideo' : 'HtmlAudio']({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'object') {
                    if (isRelationUrl(node.getAttribute('data'))) {
                        outgoingRelations.push(new relations.HtmlObject({
                            from: this,
                            to: {
                                url: node.getAttribute('data')
                            },
                            node: node,
                            attributeName: 'data'
                        }));
                    }
                } else if (nodeName === 'param' && /^(?:src|movie)$/i.test(node.getAttribute('name')) && node.parentNode && node.parentNode.nodeName.toLowerCase() === 'object') {
                    if (isRelationUrl(node.getAttribute('value'))) {
                        outgoingRelations.push(new relations.HtmlObject({
                            from: this,
                            to: {
                                url: node.getAttribute('value')
                            },
                            node: node,
                            attributeName: 'value'
                        }));
                    }
                } else if (nodeName === 'applet') {
                    ['archive', 'codebase'].forEach(function (attributeName) {
                        // Note: Only supports one url in the archive attribute. The Html 4.01 spec says it can be a comma-separated list.
                        if (isRelationUrl(node.getAttribute(attributeName))) {
                            outgoingRelations.push(new relations.HtmlApplet({
                                from: this,
                                to: {
                                    url: node.getAttribute(attributeName)
                                },
                                node: node,
                                attributeName: attributeName
                            }));
                        }
                    });
                } else if (nodeName === 'embed') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlEmbed({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                }
                if (node.hasAttribute('style')) {
                    outgoingRelations.push(new relations.HtmlStyleAttribute({
                        from: this,
                        to: new (require('./Css'))({
                            text: 'bogusselector {' + node.getAttribute('style') + '}'
                        }),
                        node: node
                    }));
                }
            } else if (node.nodeType === node.COMMENT_NODE) {
                // <!--[if !IE]> --> ... <!-- <![endif]-->
                var matchNonInternetExplorerConditionalComment = node.nodeValue.match(/^\[if !IE\]>\s*$/);
                if (matchNonInternetExplorerConditionalComment) {
                    var queueIndex = 0,
                        foundEndMarker = false;
                    while (queueIndex < queue.length) {
                        var cursor = queue[queueIndex];
                        if (cursor.nodeType === cursor.COMMENT_NODE && /^\s*<!\[endif\]\s*$/.test(cursor.nodeValue)) {
                            var div = this.parseTree.createElement('div');
                            queue.splice(0, queueIndex).forEach(function (node) {
                                div.appendChild(node.cloneNode(true));
                            });
                            outgoingRelations.push(new relations.HtmlConditionalComment({
                                from: this,
                                to: new Html({
                                    text: div.innerHTML
                                }),
                                condition: '!IE',
                                node: node,
                                endNode: queue.shift()
                            }));
                            foundEndMarker = true;
                            break;
                        }
                        queueIndex += 1;
                    }
                    if (!foundEndMarker) {
                        console.warn("assets.Html: Skipped conditional comment because no end marker was found: " + node.nodeValue);
                    }
                } else {
                    // <!--[if IE 6]> .... <![endif]-->
                    var matchConditionalComment = node.nodeValue.match(/^\[if\s*([^\]]*)\]\>([\s\S]*)<!\[endif\]$/);
                    if (matchConditionalComment) {
                        outgoingRelations.push(new relations.HtmlConditionalComment({
                            from: this,
                            to: new Html({
                                text: matchConditionalComment[2]
                            }),
                            node: node,
                            condition: matchConditionalComment[1]
                        }));
                    }
                }
            }
        }
        return outgoingRelations;
    },

    _reformatParseTree: function (removeWhiteSpaceNodes, removeComments) {
        var q = [this.parseTree];
        while (q.length) {
            var node = q.shift();
            // Non-conditional comment or whitespace-only text node?
            if ((removeComments && node.nodeType === node.COMMENT_NODE && !/^#|^\[if|^<!\[endif\]/.test(node.nodeValue)) ||
                (removeWhiteSpaceNodes && node.nodeType === node.TEXT_NODE && /^[\r\n\s\t]*$/.test(node.nodeValue))) {

                node.parentNode.removeChild(node);
            } else {
                if (node.nodeName && node.nodeName.toLowerCase() !== 'script') {
                    for (var i = 0 ; i < node.childNodes.length ; i += 1) {
                        q.push(node.childNodes[i]);
                    }
                }
            }
        }
    },

    minify: function () {
        this._reformatParseTree(true, true);
        this.isPretty = false;
        this.markDirty();
        return this;
    },

    prettyPrint: function () {
        this._reformatParseTree(true, false);
        this.isPretty = true;
        this.markDirty();
        return this;
    }
});

// Grrr...
Html.prototype.__defineSetter__('text', Text.prototype.__lookupSetter__('text'));

module.exports = Html;
<commit_msg>assets.Html: Fixed typo in '.shtml'.<commit_after>var util = require('util'),
    _ = require('underscore'),
    jsdom = require('jsdom'),
    domtohtml = require('jsdom/lib/jsdom/browser/domtohtml'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    relations = require('../relations'),
    Asset = require('./Asset'),
    Text = require('./Text');

function Html(config) {
    Text.call(this, config);
}

util.inherits(Html, Text);

function extractEncodingFromText(text) {
    var metaCharset;
    (text.match(/<meta[^>]+>/ig) || []).forEach(function (metaTagString) {
        if (/\bhttp-equiv=([\"\']|)\s*Content-Type\s*\1/i.test(metaTagString)) {
            var matchContent = metaTagString.match(/\bcontent=([\"\']|)\s*text\/html;\s*charset=([\w\-]*)\s*\1/i);
            if (matchContent) {
                metaCharset = matchContent[2];
            }
        }
    });
    return metaCharset; // Will be undefined if not found
}

function isRelationUrl(url) {
    return url && !/^javascript:|^mailto:|^\s*$|^#/i.test(url);
}

extendWithGettersAndSetters(Html.prototype, {
    contentType: 'text/html',

    defaultExtension: '.html',

    alternativeExtensions: ['.template', '.php', '.xhtml', '.shtml'],

    isPretty: false,

    get encoding() {
        if (!this._encoding) {
            // An explicit encoding (Content-Type header, data: url charset, assetConfig) takes precedence, but if absent we should
            // look for a <meta http-equiv='Content-Type' ...> tag with a charset before falling back to the defaultEncoding (utf-8)
            if ('_text' in this) {
                this._encoding = extractEncodingFromText(this._text) || this.defaultEncoding;
            } else if (this._rawSrc) {
                this._encoding = extractEncodingFromText(this._rawSrc.toString('binary', 0, Math.min(1024, this._rawSrc.length))) || this.defaultEncoding;
            } else {
                this._encoding = this.defaultEncoding;
            }
        }
        return this._encoding;
    },

    set encoding(encoding) {
        // An intended side effect of getting this.parseTree before deleting this._rawSrc is that we're sure
        // that the raw source has been decoded into this._text before the original encoding is thrown away.
        var parseTree = this.parseTree;
        if (parseTree.head) {
            var existingMetaElements = parseTree.head.getElementsByTagName('meta'),
                contentTypeMetaElement;
            for (var i = 0 ; i < existingMetaElements.length ; i += 1) {
                var metaElement = existingMetaElements[i];
                if (/^content-type$/i.test(metaElement.getAttribute('http-equiv'))) {
                    contentTypeMetaElement = metaElement;
                    break;
                }
            }
            if (!contentTypeMetaElement) {
                contentTypeMetaElement = parseTree.createElement('meta');
                contentTypeMetaElement.setAttribute('http-equiv', 'Content-Type');
                parseTree.head.insertBefore(contentTypeMetaElement, parseTree.head.firstChild);
                this.markDirty();
            }
            if ((contentTypeMetaElement.getAttribute('content') || '').toLowerCase() !== 'text/html; charset=' + encoding) {
                contentTypeMetaElement.setAttribute('content', 'text/html; charset=' + encoding);
                this.markDirty();
            }
        }
        if (encoding !== this.encoding) {
            this._encoding = encoding;
            this.markDirty();
        }
    },

    get text() {
        if (!('_text' in this)) {
            if (this._parseTree) {
                this._text = (this._parseTree.doctype ? this._parseTree.doctype + "\n" : "") + domtohtml.domToHtml(this._parseTree, !this.isPretty);
            } else {
                this._text = this._getTextFromRawSrc();
            }
        }
        return this._text;
    },

    get parseTree() {
        if (!this._parseTree) {
            try {
                this._parseTree = jsdom.jsdom(this.text, undefined, {features: {ProcessExternalResources: [], FetchExternalResources: []}});
            } catch (e) {
                throw new Error('Parse error in ' + (this.url || 'inline Html' + (this.nonInlineAncestor ? ' in ' + this.nonInlineAncestor.url : '')) + '\n' + e.message);
            }
            // Jsdom (or its Html parser) doesn't strip the newline after the <!DOCTYPE> for some reason.
            // Issue reported here: https://github.com/tmpvar/jsdom/issues/160
            if (this._parseTree.firstChild && this._parseTree.firstChild.nodeName === '#text' && this._parseTree.firstChild.nodeValue === "\n") {
                this._parseTree.removeChild(this._parseTree.firstChild);
            }
        }
        return this._parseTree;
    },

    set parseTree(parseTree) {
        this._parseTree = parseTree;
        delete this._rawSrc;
        delete this._text;
        this.markDirty();
    },

    findOutgoingRelationsInParseTree: function () {
        var outgoingRelations = [];
        if (this.parseTree.documentElement && this.parseTree.documentElement.hasAttribute('manifest')) {
            outgoingRelations.push(new relations.HtmlCacheManifest({
                from: this,
                to: {
                    url: this.parseTree.documentElement.getAttribute('manifest')
                },
                node: this.parseTree.documentElement
            }));
        }
        var queue = [this.parseTree];
        while (queue.length) {
            var node = queue.shift();
            if (node.childNodes) {
                for (var i = node.childNodes.length - 1 ; i >= 0 ; i -= 1) {
                    queue.unshift(node.childNodes[i]);
                }
            }
            if (node.nodeType === node.ELEMENT_NODE) {
                var nodeName = node.nodeName.toLowerCase();
                if (nodeName === 'script') {
                    if (node.hasAttribute('src')) {
                        var src = node.getAttribute('src');
                        if (isRelationUrl(src)) {
                            outgoingRelations.push(new relations.HtmlScript({
                                from: this,
                                to: {
                                    url: src
                                },
                                node: node
                            }));
                            if (/(^|\/)require\.js$/.test(src) && node.hasAttribute('data-main')) {
                                outgoingRelations.push(new relations.HtmlRequireJsMain({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('data-main') + '.js'
                                    },
                                    node: node
                                }));
                            }
                        }
                    } else {
                        outgoingRelations.push(new relations.HtmlScript({
                            from: this,
                            to: new (require(node.getAttribute('type') === 'text/coffeescript' ? './CoffeeScript' : './JavaScript'))({
                                text: node.firstChild ? node.firstChild.nodeValue : ''
                            }),
                            node: node
                        }));
                    }
                } else if (nodeName === 'style') {
                    outgoingRelations.push(new relations.HtmlStyle({
                        from: this,
                        to: new (require('./Css'))({
                            text: node.firstChild ? node.firstChild.nodeValue : ''
                        }),
                        node: node
                    }));
                } else if (nodeName === 'link') {
                    if (node.hasAttribute('rel')) {
                        var rel = node.getAttribute('rel');
                        if (/(?:^| )stylesheet(?:\/less)?(?:$| )/i.test(rel) && isRelationUrl(node.getAttribute('href'))) {
                            outgoingRelations.push(new relations.HtmlStyle({
                                from: this,
                                to: {
                                    url: node.getAttribute('href')
                                },
                                node: node
                            }));
                        } else if (/(?:^| )(?:apple-touch-icon(?:-precomposed)?|icon)(?:$| )/i.test(rel)) { // Also catches rel="shortcut icon"
                            if (isRelationUrl(node.getAttribute('href'))) {
                                outgoingRelations.push(new relations.HtmlShortcutIcon({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('href')
                                    },
                                    node: node
                                }));
                            }
                        } else if (/(?:^| )apple-touch-startup-image(?:$| )/i.test(rel)) {
                            if (isRelationUrl(node.getAttribute('href'))) {
                                outgoingRelations.push(new relations.HtmlAppleTouchStartupImage({
                                    from: this,
                                    to: {
                                        url: node.getAttribute('href')
                                    },
                                    node: node
                                }));
                            }
                        } else if (/(?:^| )alternate(?:$| )/i.test(rel)) {
                            if (isRelationUrl(node.getAttribute('href'))) {
                                var assetConfig = {
                                    url: node.getAttribute('href')
                                };
                                if (node.hasAttribute('type')) {
                                    assetConfig.contentType = node.getAttribute('type');
                                }
                                outgoingRelations.push(new relations.HtmlAlternateLink({
                                    from: this,
                                    to: assetConfig,
                                    node: node
                                }));
                            }
                        }
                    }
                } else if (nodeName === 'img') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlImage({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'a') {
                    var href = node.getAttribute('href');
                    if (isRelationUrl(href)) {
                        outgoingRelations.push(new relations.HtmlAnchor({
                            from: this,
                            to: {
                                url: href
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'iframe') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlIFrame({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'frame') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlFrame({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'esi:include') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlEdgeSideInclude({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'video') {
                    if (node.hasAttribute('src')) {
                        if (isRelationUrl(node.getAttribute('src'))) {
                            outgoingRelations.push(new relations[nodeName === 'video' ? 'HtmlVideo' : 'HtmlAudio']({
                                from: this,
                                to: {
                                    url: node.getAttribute('src')
                                },
                                node: node
                            }));
                        }
                    }
                    if (node.hasAttribute('poster') && isRelationUrl(node.getAttribute('poster'))) {
                        outgoingRelations.push(new relations.HtmlVideoPoster({
                            from: this,
                            to: {
                                url: node.getAttribute('poster')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'audio') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlAudio({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (/^(?:source|track)$/.test(nodeName) && node.parentNode && /^(?:video|audio)$/i.test(node.parentNode.nodeName)) {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations[node.parentNode.nodeName.toLowerCase() === 'video' ? 'HtmlVideo' : 'HtmlAudio']({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                } else if (nodeName === 'object') {
                    if (isRelationUrl(node.getAttribute('data'))) {
                        outgoingRelations.push(new relations.HtmlObject({
                            from: this,
                            to: {
                                url: node.getAttribute('data')
                            },
                            node: node,
                            attributeName: 'data'
                        }));
                    }
                } else if (nodeName === 'param' && /^(?:src|movie)$/i.test(node.getAttribute('name')) && node.parentNode && node.parentNode.nodeName.toLowerCase() === 'object') {
                    if (isRelationUrl(node.getAttribute('value'))) {
                        outgoingRelations.push(new relations.HtmlObject({
                            from: this,
                            to: {
                                url: node.getAttribute('value')
                            },
                            node: node,
                            attributeName: 'value'
                        }));
                    }
                } else if (nodeName === 'applet') {
                    ['archive', 'codebase'].forEach(function (attributeName) {
                        // Note: Only supports one url in the archive attribute. The Html 4.01 spec says it can be a comma-separated list.
                        if (isRelationUrl(node.getAttribute(attributeName))) {
                            outgoingRelations.push(new relations.HtmlApplet({
                                from: this,
                                to: {
                                    url: node.getAttribute(attributeName)
                                },
                                node: node,
                                attributeName: attributeName
                            }));
                        }
                    });
                } else if (nodeName === 'embed') {
                    if (isRelationUrl(node.getAttribute('src'))) {
                        outgoingRelations.push(new relations.HtmlEmbed({
                            from: this,
                            to: {
                                url: node.getAttribute('src')
                            },
                            node: node
                        }));
                    }
                }
                if (node.hasAttribute('style')) {
                    outgoingRelations.push(new relations.HtmlStyleAttribute({
                        from: this,
                        to: new (require('./Css'))({
                            text: 'bogusselector {' + node.getAttribute('style') + '}'
                        }),
                        node: node
                    }));
                }
            } else if (node.nodeType === node.COMMENT_NODE) {
                // <!--[if !IE]> --> ... <!-- <![endif]-->
                var matchNonInternetExplorerConditionalComment = node.nodeValue.match(/^\[if !IE\]>\s*$/);
                if (matchNonInternetExplorerConditionalComment) {
                    var queueIndex = 0,
                        foundEndMarker = false;
                    while (queueIndex < queue.length) {
                        var cursor = queue[queueIndex];
                        if (cursor.nodeType === cursor.COMMENT_NODE && /^\s*<!\[endif\]\s*$/.test(cursor.nodeValue)) {
                            var div = this.parseTree.createElement('div');
                            queue.splice(0, queueIndex).forEach(function (node) {
                                div.appendChild(node.cloneNode(true));
                            });
                            outgoingRelations.push(new relations.HtmlConditionalComment({
                                from: this,
                                to: new Html({
                                    text: div.innerHTML
                                }),
                                condition: '!IE',
                                node: node,
                                endNode: queue.shift()
                            }));
                            foundEndMarker = true;
                            break;
                        }
                        queueIndex += 1;
                    }
                    if (!foundEndMarker) {
                        console.warn("assets.Html: Skipped conditional comment because no end marker was found: " + node.nodeValue);
                    }
                } else {
                    // <!--[if IE 6]> .... <![endif]-->
                    var matchConditionalComment = node.nodeValue.match(/^\[if\s*([^\]]*)\]\>([\s\S]*)<!\[endif\]$/);
                    if (matchConditionalComment) {
                        outgoingRelations.push(new relations.HtmlConditionalComment({
                            from: this,
                            to: new Html({
                                text: matchConditionalComment[2]
                            }),
                            node: node,
                            condition: matchConditionalComment[1]
                        }));
                    }
                }
            }
        }
        return outgoingRelations;
    },

    _reformatParseTree: function (removeWhiteSpaceNodes, removeComments) {
        var q = [this.parseTree];
        while (q.length) {
            var node = q.shift();
            // Non-conditional comment or whitespace-only text node?
            if ((removeComments && node.nodeType === node.COMMENT_NODE && !/^#|^\[if|^<!\[endif\]/.test(node.nodeValue)) ||
                (removeWhiteSpaceNodes && node.nodeType === node.TEXT_NODE && /^[\r\n\s\t]*$/.test(node.nodeValue))) {

                node.parentNode.removeChild(node);
            } else {
                if (node.nodeName && node.nodeName.toLowerCase() !== 'script') {
                    for (var i = 0 ; i < node.childNodes.length ; i += 1) {
                        q.push(node.childNodes[i]);
                    }
                }
            }
        }
    },

    minify: function () {
        this._reformatParseTree(true, true);
        this.isPretty = false;
        this.markDirty();
        return this;
    },

    prettyPrint: function () {
        this._reformatParseTree(true, false);
        this.isPretty = true;
        this.markDirty();
        return this;
    }
});

// Grrr...
Html.prototype.__defineSetter__('text', Text.prototype.__lookupSetter__('text'));

module.exports = Html;
<|endoftext|>


<commit_before>wtn = {};

wtn.singles = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"];
wtn.singles = {
                  'one':        1,
                  'two':        2,
                  'three':      3,
                  'four':       4,
                  'five':       5,
                  'six':        6,
                  'seven':      7,
                  'eight':      8,
                  'nine':       9,
                  'ten':       10,
                  'eleven':    11,
                  'twelve':    12,
                  'thirteen':  13,
                  'fourteen':  14,
                  'fifteen':   15,
                  'sixteen':   16,
                  'seventeen': 17,
                  'eighteen':  18,
                  'nineteen':  19
              };

wtn.tens = {
               'twenty':  20,
               'thirty':  30,
               'forty':   40,
               'fifty':   50,
               'sixty':   60,
               'seventy': 70,
               'eighty':  80,
               'ninety':  90
           };

wtn.complicated_numbers = {
                             'million':  1000000,
                             'thousand': 1000,
                             'hundred':  100,
                          };

wtn.convert = function(input)
{
    var handleComplicatedNumber = function(phrase)
    {
        for(var key in wtn.complicated_numbers){
            if (phrase.indexOf(key) > -1)
            {
                var words = phrase.split(' ');
                words.pop();
                return wtn.convert(words.join(' ')) * wtn.complicated_numbers[key];
            }
        }
        return 0;
    }

    var phrases = input.match(/([a-z ]+ million|[a-z ]+ thousand|[a-z ]+ hundred|[a-z]+)/gi);
    var total = 0;

    for (var i=0; i < phrases.length; i++){
        var phrase = phrases[i].toLowerCase();

        total += handleComplicatedNumber(phrase);
        if (this.tens[phrase] != undefined)
            total += this.tens[phrase];
        if (this.singles[phrase] != undefined)
            total += this.singles[phrase];
    }
    return total;
}
<commit_msg>Build the regex from the data.<commit_after>wtn = {};

wtn.singles = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"];
wtn.singles = {
                  'one':        1,
                  'two':        2,
                  'three':      3,
                  'four':       4,
                  'five':       5,
                  'six':        6,
                  'seven':      7,
                  'eight':      8,
                  'nine':       9,
                  'ten':       10,
                  'eleven':    11,
                  'twelve':    12,
                  'thirteen':  13,
                  'fourteen':  14,
                  'fifteen':   15,
                  'sixteen':   16,
                  'seventeen': 17,
                  'eighteen':  18,
                  'nineteen':  19
              };

wtn.tens = {
               'twenty':  20,
               'thirty':  30,
               'forty':   40,
               'fifty':   50,
               'sixty':   60,
               'seventy': 70,
               'eighty':  80,
               'ninety':  90
           };

wtn.complicated_numbers = {
                             'million':  1000000,
                             'thousand': 1000,
                             'hundred':  100,
                          };

wtn.convert = function(input)
{
    var handleComplicatedNumber = function(phrase)
    {
        for(var key in wtn.complicated_numbers){
            if (phrase.indexOf(key) > -1)
            {
                var words = phrase.split(' ');
                words.pop();
                return wtn.convert(words.join(' ')) * wtn.complicated_numbers[key];
            }
        }
        return 0;
    }

    var regex = [];
    for(var key in wtn.complicated_numbers)
        regex.push("[a-z ]+ " + key);
    regex.push("[a-z]+");
    regex = "(" + regex.join('|') + ")";
    regex = new RegExp(regex, "gi");
    var phrases = input.match(regex);
    var total = 0;

    for (var i=0; i < phrases.length; i++){
        var phrase = phrases[i].toLowerCase();

        total += handleComplicatedNumber(phrase);
        if (this.tens[phrase] != undefined)
            total += this.tens[phrase];
        if (this.singles[phrase] != undefined)
            total += this.singles[phrase];
    }
    return total;
}
<|endoftext|>


<commit_before>// var Adapter     = require('../adapter');
var RSVP        = require('rsvp');
var azure       = require('azure-storage');
var chalk       = require('chalk');
var Promise     = require('ember-cli/lib/ext/promise');
var SilentError = require('ember-cli/lib/errors/silent');
var Adapter     = require('ember-deploy/utilities/adapter')

var DEFAULT_MANIFEST_SIZE   = 10;
var DEFAULT_TAGGING_ADAPTER = 'sha';

var green = chalk.green;
var white = chalk.white;

var AZURE_TABLE_NAME = 'emberdeploy';
var AZURE_MANIFEST_TAG = 'manifest';

module.exports = Adapter.extend({
  init: function() {
    this.tagging      = this.tagging || DEFAULT_TAGGING_ADAPTER;
    this.manifestSize = this.manifestSize || DEFAULT_MANIFEST_SIZE;

    // be transparant to either passing a connectionString or the storageAccount + storageAccessKey
    if(this.config.connectionString) {
      this.client = azure.createTableService(this.config.connectionString);
    } else if (this.config.storageAccount && this.config.storageAccessKey) {
      this.client = azure.createTableService(this.config.storageAccount, this.config.storageAccessKey);
    } else {
      console.error("No connection string or storage account plus access key set for this Azure deployment.");
      // TODO throw error

      return false;
    }

    /*
      TODO to make feasible for tests;
      In order to run the tests, the following environment variables need to be set up:
      AZURE_STORAGE_CONNECTION_STRING="valid storage connection string"
    */

    // TODO createTableIfNotExists on async calls
    /*
    PartitionKey: entGen.String(AZURE_CURRENT_TAG),
    RowKey: entGen.String('<manifest>')
    */
  },

  upload: function(value) {
    var taggingAdapter = this.taggingAdapter || this._initTaggingAdapter();
    var key            = taggingAdapter.createTag();

    return this._upload(value, key);
  },

  list: function() {
    return RSVP.hash({
      revisions: this._list(),
      current: this._current()
    })
    .then(function(results) {
      var revisions = results.revisions;
      var current   = results.current;
      var message   = this._revisionListMessage(revisions, current);

      this._printSuccessMessage(message);

      return message;
    }.bind(this));
  },

  activate: function(revisionKey) {
    if (!revisionKey) {
      return this._printErrorMessage(this._noRevisionPassedMessage());
    };

    var uploadKey = this._currentKey();
    var that      = this;

    return new RSVP.Promise(function(resolve, reject) {
      that._list()
        .then(function(uploads) {
          return uploads.indexOf(revisionKey) > -1 ? resolve() : reject();
        })
        .then(function() {
          return that.client.set(uploadKey, revisionKey);
        })
        .then(resolve);
    })
    .then(this._activationSuccessfulMessage)
    .then(this._printSuccessMessage.bind(this))
    .catch(function() {
      return this._printErrorMessage(this._revisionNotFoundMessage());
    }.bind(this));
  },

  _list: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error) {
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _current: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          // find the current tag
          this.client.retrieveEntity(AZURE_TABLE_NAME, AZURE_MANIFEST_TAG, that._currentKey(), function(error, result, response){
            if(!error){
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _initTaggingAdapter: function() {
    var TaggingAdapter = require('ember-deploy/utilities/tagging/'+this.tagging);

    return new TaggingAdapter({
      manifest: this.manifest
    });
  },

  _upload: function(value, key) {
    return this._uploadIfNotAlreadyInManifest(value, key)
      .then(this._updateManifest.bind(this, this.manifest, key))
      .then(this._cleanUpManifest.bind(this))
      .then(this._deploySuccessMessage.bind(this, key))
      .then(this._printSuccessMessage.bind(this))
      .then(function() { return key; })
      .catch(function() {
        var message = this._deployErrorMessage();
        return this._printErrorMessage(message);
      }.bind(this));
  },

  _uploadIfNotAlreadyInManifest: function(value, key) {
    var that = this;

    return;

    // TODO actual implementation
    return new RSVP.Promise(function(resolve, reject) {
      that.client.get(key)
        .then(function(result) {
          result === null ? resolve() : reject();
        })
        .then(function() {
          return that.client.set(key, value);
        })
        .then(resolve);
    });
  },

  _updateManifest: function(manifest, key) {
    // TODO actual implementation

    return;
    return this.client.lpush(manifest, key);
  },

  _cleanUpManifest: function() {
    // TODO actual implementation

    return;
    return this.client.ltrim(this.manifest, 0, this.manifestSize - 1);
  },

  _currentKey: function() {
    return this.manifest+':current';
  },

  _printSuccessMessage: function(message) {
    return this.ui.writeLine(message);
  },

  _printErrorMessage: function(message) {
    return Promise.reject(new SilentError(message));
  },

  _deploySuccessMessage: function(revisionKey) {
    var success       = green('\nUpload successful!\n\n');
    var uploadMessage = white('Uploaded revision: ')+green(revisionKey);

    return success + uploadMessage;
  },

  _deployErrorMessage: function() {
    var failure    = '\nUpload failed!\n';
    var suggestion = 'Did you try to upload an already uploaded revision?\n\n';
    var solution   = 'Please run `'+green('ember deploy:list')+'` to ' +
                     'investigate.';

    return failure + '\n' + white(suggestion) + white(solution);
  },

  _noRevisionPassedMessage: function() {
    var err = '\nError! Please pass a revision to `deploy:activate`.\n\n';

    return err + white(this._revisionSuggestion());
  },

  _activationSuccessfulMessage: function() {
    var success = green('\nActivation successful!\n\n');
    var message = white('Please run `'+green('ember deploy:list')+'` to see '+
                        'what revision is current.');

    return success + message;
  },

  _revisionNotFoundMessage: function() {
    var err = '\nError! Passed revision could not be found in manifest!\n\n';

    return err + white(this._revisionSuggestion());
  },

  _revisionSuggestion: function() {
    var suggestion = 'Try to run `'+green('ember deploy:list')+'` '+
                     'and pass a revision listed there to `' +
                     green('ember deploy:activate')+'`.\n\nExample: \n\n'+
                     'ember deploy:activate --revision <manifest>:<sha>';

    return suggestion;
  },

  _revisionListMessage: function(revisions, currentRevision) {
    var manifestSize  = this.manifestSize;
    var headline      = '\nLast '+ manifestSize + ' uploaded revisions:\n\n';
    var footer        = '\n\n# => - current revision';
    var revisionsList = revisions.reduce(function(prev, curr) {
      var prefix = (curr === currentRevision) ? '| => ' : '|    ';
      return prev + prefix + chalk.green(curr) + '\n';
    }, '');

    return headline + revisionsList + footer;
  }
});
<commit_msg>Upload index html<commit_after>// var Adapter     = require('../adapter');
var RSVP        = require('rsvp');
var azure       = require('azure-storage');
var chalk       = require('chalk');
var Promise     = require('ember-cli/lib/ext/promise');
var SilentError = require('ember-cli/lib/errors/silent');
var Adapter     = require('ember-deploy/utilities/adapter')

var DEFAULT_MANIFEST_SIZE   = 10;
var DEFAULT_TAGGING_ADAPTER = 'sha';

var green = chalk.green;
var white = chalk.white;

var AZURE_TABLE_NAME = 'emberdeploy';
var AZURE_MANIFEST_TAG = 'manifest';

module.exports = Adapter.extend({
  init: function() {
    this.tagging      = this.tagging || DEFAULT_TAGGING_ADAPTER;
    this.manifestSize = this.manifestSize || DEFAULT_MANIFEST_SIZE;

    // be transparant to either passing a connectionString or the storageAccount + storageAccessKey
    if(this.config.connectionString) {
      this.client = azure.createTableService(this.config.connectionString);
    } else if (this.config.storageAccount && this.config.storageAccessKey) {
      this.client = azure.createTableService(this.config.storageAccount, this.config.storageAccessKey);
    } else {
      console.error("No connection string or storage account plus access key set for this Azure deployment.");
      // TODO throw error

      return false;
    }

    /*
      TODO to make feasible for tests;
      In order to run the tests, the following environment variables need to be set up:
      AZURE_STORAGE_CONNECTION_STRING="valid storage connection string"
    */
  },

  upload: function(value) {
    var taggingAdapter = this.taggingAdapter || this._initTaggingAdapter();
    var key            = taggingAdapter.createTag();

    return this._upload(value, key);
  },

  list: function() {
    return RSVP.hash({
      revisions: this._list(),
      current: this._current()
    })
    .then(function(results) {
      var revisions = results.revisions;
      var current   = results.current;
      var message   = this._revisionListMessage(revisions, current);

      this._printSuccessMessage(message);

      return message;
    }.bind(this));
  },

  activate: function(revisionKey) {
    if (!revisionKey) {
      return this._printErrorMessage(this._noRevisionPassedMessage());
    };

    var uploadKey = this._currentKey();
    var that      = this;

    return new RSVP.Promise(function(resolve, reject) {
      that._list()
        .then(function(uploads) {
          return uploads.indexOf(revisionKey) > -1 ? resolve() : reject();
        })
        .then(function() {
          return that.client.set(uploadKey, revisionKey);
        })
        .then(resolve);
    })
    .then(this._activationSuccessfulMessage)
    .then(this._printSuccessMessage.bind(this))
    .catch(function() {
      return this._printErrorMessage(this._revisionNotFoundMessage());
    }.bind(this));
  },

  _list: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error) {
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _current: function() {
    var that = this;

    return new Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          // find the current tag
          this.client.retrieveEntity(AZURE_TABLE_NAME, AZURE_MANIFEST_TAG, that._currentKey(), function(error, result, response){
            if(!error){
              resolve(result.entries);
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _initTaggingAdapter: function() {
    var TaggingAdapter = require('ember-deploy/utilities/tagging/'+this.tagging);

    return new TaggingAdapter({
      manifest: this.manifest
    });
  },

  _upload: function(value, key) {
    return this._uploadIfNotAlreadyInManifest(value, key)
      .then(this._updateManifest.bind(this, this.manifest, key))
      .then(this._cleanUpManifest.bind(this))
      .then(this._deploySuccessMessage.bind(this, key))
      .then(this._printSuccessMessage.bind(this))
      .then(function() { return key; })
      .catch(function() {
        var message = this._deployErrorMessage();
        return this._printErrorMessage(message);
      }.bind(this));
  },

  _uploadIfNotAlreadyInManifest: function(value, key) {
    // read the buffer
    value = value.toString();
    var that = this;

    return new RSVP.Promise(function(resolve, reject) {
      // create table if not already existent
      that.client.createTableIfNotExists(AZURE_TABLE_NAME, function(error, result, response) {
        if(!error){
          var query = new azure.TableQuery()
                  .where('PartitionKey eq ?', AZURE_MANIFEST_TAG)
                  .and('RowKey eq ?', key);

          // find the list of uploaded revisions
          that.client.queryEntities(AZURE_TABLE_NAME, query, null, function(error, result, response) {
            if(!error){
              // has this key already been uploaded once?
              if(result.entries.length > 0) {
                reject("Key already in manifest - revision already uploaded or collided.");
              } else {
                var entGen = azure.TableUtilities.entityGenerator;
                var entity = {};
                entity["PartitionKey"] = entGen.String(AZURE_MANIFEST_TAG);
                entity["RowKey"] = entGen.String(key);
                entity["content"] = entGen.String(value);

                that.client.insertEntity(AZURE_TABLE_NAME, entity,  function (error, result, response) {
                  if(!error){
                    resolve(result);
                  } else {
                    reject(error);
                  }
                });
              }
            } else {
              reject(error);
            }
          });
        } else {
          reject(error);
        }
      });
    });
  },

  _updateManifest: function(manifest, key) {
    // TODO actual implementation

    return;
    return this.client.lpush(manifest, key);
  },

  _cleanUpManifest: function() {
    // TODO actual implementation

    return;
    return this.client.ltrim(this.manifest, 0, this.manifestSize - 1);
  },

  _currentKey: function() {
    return this.manifest+':current';
  },

  _printSuccessMessage: function(message) {
    return this.ui.writeLine(message);
  },

  _printErrorMessage: function(message) {
    return Promise.reject(new SilentError(message));
  },

  _deploySuccessMessage: function(revisionKey) {
    var success       = green('\nUpload successful!\n\n');
    var uploadMessage = white('Uploaded revision: ')+green(revisionKey);

    return success + uploadMessage;
  },

  _deployErrorMessage: function() {
    var failure    = '\nUpload failed!\n';
    var suggestion = 'Did you try to upload an already uploaded revision?\n\n';
    var solution   = 'Please run `'+green('ember deploy:list')+'` to ' +
                     'investigate.';

    return failure + '\n' + white(suggestion) + white(solution);
  },

  _noRevisionPassedMessage: function() {
    var err = '\nError! Please pass a revision to `deploy:activate`.\n\n';

    return err + white(this._revisionSuggestion());
  },

  _activationSuccessfulMessage: function() {
    var success = green('\nActivation successful!\n\n');
    var message = white('Please run `'+green('ember deploy:list')+'` to see '+
                        'what revision is current.');

    return success + message;
  },

  _revisionNotFoundMessage: function() {
    var err = '\nError! Passed revision could not be found in manifest!\n\n';

    return err + white(this._revisionSuggestion());
  },

  _revisionSuggestion: function() {
    var suggestion = 'Try to run `'+green('ember deploy:list')+'` '+
                     'and pass a revision listed there to `' +
                     green('ember deploy:activate')+'`.\n\nExample: \n\n'+
                     'ember deploy:activate --revision <manifest>:<sha>';

    return suggestion;
  },

  _revisionListMessage: function(revisions, currentRevision) {
    var manifestSize  = this.manifestSize;
    var headline      = '\nLast '+ manifestSize + ' uploaded revisions:\n\n';
    var footer        = '\n\n# => - current revision';
    var revisionsList = revisions.reduce(function(prev, curr) {
      var prefix = (curr === currentRevision) ? '| => ' : '|    ';
      return prev + prefix + chalk.green(curr) + '\n';
    }, '');

    return headline + revisionsList + footer;
  }
});
<|endoftext|>