<commit_before>var requestIntervalAjaxCall;
var myID;

workflowItem = Backbone.Model.extend({
    url         : function (completeURL) {
	if (completeURL == null) {
	    var targetURL = this.attributes.baseURL;
	    targetURL += this.attributes.actionURL + this.id + '/';
	    if (this.attributes.actionURL == "validate/") {
		targetURL += this.attributes.state;
	    }
	    return targetURL;
	}
	return completeURL;
    },
    validate    : function () {
    },
    initialize  : function (options) {
	this.attributes.owner = options.owner ? options.owner.data.trim() : null;
	this.id = this.attributes.detailURL.split('/')[this.attributes.detailURL.split('/').length - 2];
	this.attributes.baseURL = "/workflow/item/";
	this.attributes.actionURL = null;
	this.attributes.ajaxCallback = null;
	$("tr#detail-item-" + this.id).hide();
	$("tr#detail-item-" + this.id).css("visibility", "hidden");
    }
});

workflowCategory = Backbone.Model.extend({
    url         : function () {
	var targetURL = this.attributes.baseURL;
	targetURL += this.attributes.actionURL;
	targetURL += this.id + '/';
	return targetURL;
    },
    initialize  : function () {
	this.attributes.baseURL = "/workflow/category/";
	this.attributes.actionURL = null;
	this.attributes.workflowId = $("div.categories_table_workflow").attr("id").split('-')[1];
	this.id = this.attributes.categoryId;
    }
});

workflowItemCollection = Backbone.Collection.extend({
    model   :   workflowItem
});

workflowItemView = Backbone.View.extend({
    events  : {
	"click a.validation-disabled-None"  :       "resetItemState",
	"click a.validation-disabled-OK"    :       "updateItemState",
	"click a.validation-disabled-KO"    :       "updateItemState",
	"click td.take-item"                :       "takeOrUntakeOneItem",
	"click td.untake-item"              :       "takeOrUntakeOneItem",
	"click a.label_item"                :       "getDetailItem"
    },
    updateItemState : function (e) {
	this.model.set({actionURL : "validate/"});
	this.model.set({state : $(e.target).parent().attr("class").split(' ')[0].split('-')[2]});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		_update_item_shortcut(resp, model.url(), $(e.target).parent());
	    },
	    error   : function (model) {
		model.set({state : model.get("state") == "OK" ? "KO" : "OK"});
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 0);
    },
    resetItemState  : function (e) {
	this.model.set({actionURL : "no_state/"});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		model.set({state : "None"});
		_update_item_shortcut(resp, model.url(), $(e.target));
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 0);
    },
    takeOrUntakeOneItem    : function (e) {
	var actionOnItem = $(e.target).parents("td").attr("class").split('-')[0] + '/';
	this.model.set({actionURL : actionOnItem});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		if (actionOnItem == "take/") {
		    _update_item_add_owner(resp, model.url(), $(e.target).parents("td"));
		} else {
		    _update_item_reset_owner(resp, model.url(), $(e.target).parents("td"));
		}
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 1);
    },
    getDetailItem       : function (e) {
	var prop = $("tr#detail-item-" + this.model.id).css("visibility");
	if (prop == "visible") {
	    $("tr#detail-item-" + this.model.id).css("visibility", "hidden");
	    $("tr#detail-item-" + this.model.id).fadeOut();
	} else {
	    $("tr#detail-item-" + this.model.id).css("visibility", "visible");
	    $("tr#detail-item-" + this.model.id).fadeIn();
	    var targetSection = $(e.target).parents("tr").next().find("div.title_detail_item a")[0];
	    _show_commentOrDetail(targetSection, 'detail');
	    _show_item_detail(this.model.url(this.model.get("detailURL")), $("tr#detail-item-" + this.model.id));
	}
    },
    initialize  : function () {
    }
});

workflowCategoryView = Backbone.View.extend({
    events      : {
	"click td.take_untake_group"    :   "takeOrUntakeGroupOfItem"
    },
    takeOrUntakeGroupOfItem : function (e) {
	var actionOnGroup = $(e.target).attr("class").split('-')[0] + '/';
	this.model.set({actionURL : actionOnGroup});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		if (actionOnGroup == "take/") {
		    _update_whole_group_add_owner(resp);
		} else {
		    _update_whole_group_reset_owner(resp);
		}
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("table"), 2);
    },
    initialize   : function () {
	this.viewCollection = [];
    }
});

function generateBackboneModelsCollection() {
    // Generate view/models for categories
    var allCategoriesLines = $("tr.category-header");
    for (i = 0; i < allCategoriesLines.length; i++) {
	var categoryName = $(allCategoriesLines[i]).find("th").html();
	var categoryId = $(allCategoriesLines[i]).parents("table").attr("id").split('-')[1];
	var modelToAdd = new workflowCategory({label : categoryName, categoryId : categoryId});
	var currentCategory = new workflowCategoryView({el : $(allCategoriesLines[i]), model : modelToAdd});
	var allItemLines = $(allCategoriesLines[i]).parents("table").find("tr.highlight");
	for (y = 0; y < allItemLines.length; y++) {
	    var labelItem = $(allItemLines[y]).find("a.label_item").html();
	    var itemID = $(allItemLines[y]).find("td.label").attr("id").split('-')[2];
	    var detailItemURL = "/workflow/item/show/" + itemID + '/';
	    var ownerItem = $(allItemLines[y]).find("td.untake-item").contents()[0];
	    var stateItem = $(allItemLines[y]).find("td.shortcut-cell").attr("class").split(' ')[0].split('-')[2];
	    var modelToAddItem = new workflowItem({label : labelItem, detailURL : detailItemURL, owner : ownerItem, state : stateItem});
	    var viewToAdd = new workflowItemView({el : $(allItemLines[y]), model : modelToAddItem});
	    currentCategory.viewCollection.push(viewToAdd);
	}
    }
}

itemsCollection = new workflowItemCollection();
generateBackboneModelsCollection();

$(document).ready(function () {
    Backbone.emulateHTTP = true;

    $('#progress_bar').append(progressBar);
    update_statistics_progressbar();
    update_statistics_filters();

    intervalAjaxCall();
});
<commit_msg>Comment useless 'workflowItemCollection' and rename it to 'workflowColection'<commit_after>var requestIntervalAjaxCall;
var myID;

workflowItem = Backbone.Model.extend({
    url         : function (completeURL) {
	if (completeURL == null) {
	    var targetURL = this.attributes.baseURL;
	    targetURL += this.attributes.actionURL + this.id + '/';
	    if (this.attributes.actionURL == "validate/") {
		targetURL += this.attributes.state;
	    }
	    return targetURL;
	}
	return completeURL;
    },
    validate    : function () {
    },
    initialize  : function (options) {
	this.attributes.owner = options.owner ? options.owner.data.trim() : null;
	this.id = this.attributes.detailURL.split('/')[this.attributes.detailURL.split('/').length - 2];
	this.attributes.baseURL = "/workflow/item/";
	this.attributes.actionURL = null;
	this.attributes.ajaxCallback = null;
	$("tr#detail-item-" + this.id).hide();
	$("tr#detail-item-" + this.id).css("visibility", "hidden");
    }
});

workflowCategory = Backbone.Model.extend({
    url         : function () {
	var targetURL = this.attributes.baseURL;
	targetURL += this.attributes.actionURL;
	targetURL += this.id + '/';
	return targetURL;
    },
    initialize  : function () {
	this.attributes.baseURL = "/workflow/category/";
	this.attributes.actionURL = null;
	this.attributes.workflowId = $("div.categories_table_workflow").attr("id").split('-')[1];
	this.id = this.attributes.categoryId;
    }
});

/* Unused for now
workflowCollection = Backbone.Collection.extend({
});
*/

workflowItemView = Backbone.View.extend({
    events  : {
	"click a.validation-disabled-None"  :       "resetItemState",
	"click a.validation-disabled-OK"    :       "updateItemState",
	"click a.validation-disabled-KO"    :       "updateItemState",
	"click td.take-item"                :       "takeOrUntakeOneItem",
	"click td.untake-item"              :       "takeOrUntakeOneItem",
	"click a.label_item"                :       "getDetailItem"
    },
    updateItemState : function (e) {
	this.model.set({actionURL : "validate/"});
	this.model.set({state : $(e.target).parent().attr("class").split(' ')[0].split('-')[2]});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		_update_item_shortcut(resp, model.url(), $(e.target).parent());
	    },
	    error   : function (model) {
		model.set({state : model.get("state") == "OK" ? "KO" : "OK"});
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 0);
    },
    resetItemState  : function (e) {
	this.model.set({actionURL : "no_state/"});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		model.set({state : "None"});
		_update_item_shortcut(resp, model.url(), $(e.target));
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 0);
    },
    takeOrUntakeOneItem    : function (e) {
	var actionOnItem = $(e.target).parents("td").attr("class").split('-')[0] + '/';
	this.model.set({actionURL : actionOnItem});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		if (actionOnItem == "take/") {
		    _update_item_add_owner(resp, model.url(), $(e.target).parents("td"));
		} else {
		    _update_item_reset_owner(resp, model.url(), $(e.target).parents("td"));
		}
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("td"), 1);
    },
    getDetailItem       : function (e) {
	var prop = $("tr#detail-item-" + this.model.id).css("visibility");
	if (prop == "visible") {
	    $("tr#detail-item-" + this.model.id).css("visibility", "hidden");
	    $("tr#detail-item-" + this.model.id).fadeOut();
	} else {
	    $("tr#detail-item-" + this.model.id).css("visibility", "visible");
	    $("tr#detail-item-" + this.model.id).fadeIn();
	    var targetSection = $(e.target).parents("tr").next().find("div.title_detail_item a")[0];
	    _show_commentOrDetail(targetSection, 'detail');
	    _show_item_detail(this.model.url(this.model.get("detailURL")), $("tr#detail-item-" + this.model.id));
	}
    },
    initialize  : function () {
    }
});

workflowCategoryView = Backbone.View.extend({
    events      : {
	"click td.take_untake_group"    :   "takeOrUntakeGroupOfItem"
    },
    takeOrUntakeGroupOfItem : function (e) {
	var actionOnGroup = $(e.target).attr("class").split('-')[0] + '/';
	this.model.set({actionURL : actionOnGroup});
	this.model.set({ajaxCallback : {
	    success : function (model, resp) {
		if (actionOnGroup == "take/") {
		    _update_whole_group_add_owner(resp);
		} else {
		    _update_whole_group_reset_owner(resp);
		}
	    },
	    error   : function () {
		displayError(titleErrorHappened, errorHappened);
	    }
	}});
	_item_has_changed(this.model, $(e.target).parents("table"), 2);
    },
    initialize   : function () {
	this.viewCollection = [];
    }
});

function generateBackboneModelsCollection() {
    // Generate view/models for categories
    var allCategoriesLines = $("tr.category-header");
    for (i = 0; i < allCategoriesLines.length; i++) {
	var categoryName = $(allCategoriesLines[i]).find("th").html();
	var categoryId = $(allCategoriesLines[i]).parents("table").attr("id").split('-')[1];
	var modelToAdd = new workflowCategory({label : categoryName, categoryId : categoryId});
	var currentCategory = new workflowCategoryView({el : $(allCategoriesLines[i]), model : modelToAdd});
	var allItemLines = $(allCategoriesLines[i]).parents("table").find("tr.highlight");
	for (y = 0; y < allItemLines.length; y++) {
	    var labelItem = $(allItemLines[y]).find("a.label_item").html();
	    var itemID = $(allItemLines[y]).find("td.label").attr("id").split('-')[2];
	    var detailItemURL = "/workflow/item/show/" + itemID + '/';
	    var ownerItem = $(allItemLines[y]).find("td.untake-item").contents()[0];
	    var stateItem = $(allItemLines[y]).find("td.shortcut-cell").attr("class").split(' ')[0].split('-')[2];
	    var modelToAddItem = new workflowItem({label : labelItem, detailURL : detailItemURL, owner : ownerItem, state : stateItem});
	    var viewToAdd = new workflowItemView({el : $(allItemLines[y]), model : modelToAddItem});
	    currentCategory.viewCollection.push(viewToAdd);
	}
    }
}

generateBackboneModelsCollection();

$(document).ready(function () {
    Backbone.emulateHTTP = true;

    $('#progress_bar').append(progressBar);
    update_statistics_progressbar();
    update_statistics_filters();

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


<commit_before>'use strict'

const {type} = require('message-type')
const canvasApi = require('../canvasApi')
const Promise = require('bluebird')
const cl = require('../azureStorage')
const config = require('../server/init/configuration')
const log = require('../server/init/logging')
require('colors')

const csvVol = config.secure.azure.csvBlobName
const msgVol = config.secure.azure.msgBlobName
const csvDir = config.secure.localFile.csvDir
const lmsDatabase = config.secure.azure.databaseName
const lmsCollection = config.secure.azure.collectionName

function _createCsvFile (msg, sisCourseCode, enrollmentsArray, timeStamp) {
  let header = 'course_id,user_id,role,status\n'
  let msgtype = msg._desc.userType
  let csvFileName = 'enrollments.' + msgtype + '.' + sisCourseCode + '.' + timeStamp + '.csv'
  let msgFileName = 'enrollments.' + msgtype + '.' + sisCourseCode + '.' + timeStamp + '.msg'
  let csvString = ''
  let msgUsers = new Set([])
  let canvasUsers = new Set([])
  let canvasUserArray = []
  let csvArray = []
  let csvDarray = []

  log.info('Number of users recived from canvas: ' + enrollmentsArray.length)
  log.info('Number of users recieved from message: ' + msg.member.length)

  if (msg.member && msg.member.length > 0) {
    msgUsers = new Set(msg.member)
  }
  canvasUserArray = enrollmentsArray.map(canvasUser => canvasUser.sis_user_id)

  canvasUserArray.forEach(item => log.info('Debug canvas userid: ' + item))
  msg.member.forEach(item => log.info('debug msg userid: ' + item))

  if (canvasUserArray && canvasUserArray.length > 0) {
    canvasUsers = new Set(canvasUserArray)
  }
  let activateSet = new Set([...msgUsers].filter(user => !canvasUsers.has(user)))
  let deactivateSet = new Set([...canvasUsers].filter(user => !msgUsers.has(user)))
  let counter = 0
  csvArray = [...activateSet].map(user => {
    counter += 1
    log.info('[' + counter + '] ' + 'User: ' + user + ' will be enrolled to course: ' + sisCourseCode)
    return {
      course_id: sisCourseCode,
      user_id: user,
      role: msgtype,
      status: 'active'
    }
  })
  counter = 0
  csvDarray = [...deactivateSet].map(user => {
    counter += 1
    log.info('[' + counter + '] ' + 'User: ' + user + ' should be unrolled from course ' + sisCourseCode + ' ,no action taken for now: ')
    return {
      course_id: sisCourseCode,
      user_id: user,
      role: msgtype,
      status: 'deactivate'
    }
  })
  csvDarray = [] // Just to shut up the linter....
  log.info(csvDarray) // Just to shut up the linter....
  csvArray.forEach(csvRow => {
    csvString = csvString + `${csvRow.course_id},${csvRow.user_id},${csvRow.role},${csvRow.status}\n`
  })
  let csvData = header + csvString
  log.info('\nGoing to open file: ' + csvFileName + ' ' + msgFileName)

  if (csvString === '') {
    csvFileName = csvFileName + '.EMPTY'
  }

  return cl.cloudStoreTextToFile(csvFileName, csvVol, csvData)
  .then(result => { log.info(result); return cl.cloudStoreTextToFile(msgFileName, msgVol, JSON.stringify(msg, null, 4)) })
  .then(() => cl.cloudgetFile(csvFileName, csvVol, csvDir))
  .then(result => { log.info(result); let fileName = csvDir + result.name; return fileName })
  .catch(error => { log.error(error); return Promise.reject(error) })
}

function _parseKeyStudent (key) {
  // ladok2.kurser.DM.2517.registrerade_20162.1
  let course = null
  let termin = null
  let year = null
  let ladok = null
  let myRe = /^(\w+).(\w+).(\w+).(\w+).(\w+)_(\d\d)(\d\d)(\d).(\d+)/g
  let myArray = myRe.exec(key)
  if (myArray != null) {
    let courseInOne = 3
    let courseInTwo = 4
    let terminIn = 8
    let yearIn = 7
    let ladokIn = 9
    course = myArray[courseInOne] + myArray[courseInTwo]
    termin = myArray[terminIn] === 1 ? 'VT' : 'HT'
    year = myArray[yearIn]
    ladok = myArray[ladokIn]
    let sisCourseCode = course + termin + year + ladok
    return Promise.resolve(sisCourseCode)
  }
  return Promise.reject(Error('_parseKeyStudent, ' + key + ' could not be decoded'))
}

function _parseKeyTeacher (key) {
   // edu.courses.AE.AE2302.20162.1.teachers edu.courses.DD.DD1310.20162.1.assistants
  let course = null
  let termin = null
  let year = null
  let ladok = null
  let courseIn = 2
  let terminIn = 5
  let yearIn = 4
  let ladokIn = 6
  let myRe = /^edu.courses.(\w+).(\w+).(\d\d)(\d\d)(\d).(\d).(\w+)$/g
  let myArray = myRe.exec(key)
  if (myArray != null) {
    course = myArray[courseIn]
    termin = myArray[terminIn] === 1 ? 'VT' : 'HT'
    year = myArray[yearIn]
    ladok = myArray[ladokIn]
    let sisCourseCode = course + termin + year + ladok
    return Promise.resolve(sisCourseCode)
  }
  return Promise.reject(Error('_parseKeyTeacher, ' + key + ' could not be decoded'))
}

function _parseKey (key, msgtype) {
  if (msgtype === type.students) {
    return _parseKeyStudent(key)
  }
  if (msgtype === type.teachers || msgtype === type.assistants) {
    return _parseKeyTeacher(key)
  } else {
    return Promise.reject(Error('_parseKey unkown type, ' + msgtype))
  }
}

function _getEnrollmentsForCourse (canvasCourseId, msgtype) {
  let enrollType = ''
  switch (msgtype) {
    case type.students:
      enrollType = 'StudentEnrollment'
      break
    case type.teachers:
      enrollType = 'TeacherEnrollment'
      break
    case type.assistants:
      enrollType = 'TaEnrollment'
      break
    default:
      log.warn('enrollment type not defined....')
      return Promise.reject(Error('_getEnrollmentsForCourse, Invalid message type: ' + msgtype))
  }
  return canvasApi.getEnrollmentList(canvasCourseId, enrollType)
}

function _sendCsvIfNotEmpty (fileName) {
  if (fileName.split('.')[6] === 'EMPTY') {
    log.info('CSV file is empty, will not be sent to canvas, skipping....')
    return
  } else {
    return canvasApi.sendCsvFile(fileName)
  }
}

function _followUpTicket (csvTicket) {
  if (!csvTicket) {
    return // No file was sent to canvas, file empty
  }
  let Result = JSON.parse(csvTicket)
  log.debug(csvTicket)
  return canvasApi.getCsvFileStatus(Result.id)
}

function _generateReport (csvReport, sisCourseCode, timeStamp, document) {
  setTimeout(_createReport.bind(null, csvReport, sisCourseCode, timeStamp, document), 120000)
  return Promise.resolve('Done')
}

function _createReport (csvReport, sisCourseCode, timeStamp, document) {
  let documentId = sisCourseCode + '.' + timeStamp
  if (!csvReport) {
    document['csvTicket'] = 'Empty'
    document['csvReport'] = 'Empty'
  } else {
    let tmp = JSON.parse(document.csvTicket)
    document['csvTicket'] = tmp
    document['csvReport'] = JSON.parse(csvReport)
  }
  document['id'] = documentId
  console.info(JSON.stringify(document, null, 4))
  let collectionUrl = `dbs/${lmsDatabase}/colls/${lmsCollection}`
  return cl.cloudCreateDocument(document, collectionUrl)
}

function _handleError (err, sisCourseCode) {
  if (err.statusCode === 404) {
    log.info('Course does not exist in canvas, skipping, '.red + sisCourseCode.red)
    return Promise.resolve('Course does not exist in canvas')
  } else {
    return Promise.reject(Error(err))
  }
}

function _process (msg) {
  let sisCourseCode = ''
  let timeStamp = Date.now()
  let msgtype = msg._desc.userType
  let key = msg.ug1Name
  let document = {}

  return _parseKey(key, msgtype)
    .then(sisCode => {
      sisCourseCode = sisCode
      log.info(`In _process ${sisCourseCode}, processing for ${msgtype}`)
      document['sisCourseCode'] = sisCourseCode
      return canvasApi.findCourse(sisCourseCode)
    })
    .then(result => {
      let Result = JSON.parse(result)
      document['canvasCourse'] = Result
      return _getEnrollmentsForCourse(Result.id, msgtype)
    })
    .then(enrollmentsArray => {
      document['usersInCanvas'] = enrollmentsArray.map(canvasUser => canvasUser.sis_user_id)
      document['usersInmsg'] = msg.member
      return _createCsvFile(msg, sisCourseCode, enrollmentsArray, timeStamp)
    })
    .then(fileName => { document['fileName'] = fileName; return _sendCsvIfNotEmpty(fileName) })
    .then(csvTicket => { document['csvTicket'] = csvTicket; return _followUpTicket(csvTicket) })
    .then(csvReport => _generateReport(csvReport, sisCourseCode, timeStamp, document))
    .catch(err => _handleError(err, sisCourseCode))
}

module.exports = function (msg, counter) {
  log.info('\nProcessing for msg..... ' + msg.ug1Name)
  var msgtype = msg._desc.userType
  if (msg._desc && (msgtype === type.students || msgtype === type.teachers || msgtype === type.assistants)) {
    return _process(msg)
  } else {
    log.error('\nThis is something else than students, teacher, assistant, we can probably wait with this until the students is handled', JSON.stringify(msg, null, 4))
    return Promise.resolve('Unknown flag: ' + msgtype)
  }
}
<commit_msg>fixed bug in retriving csv import repport from canvas<commit_after>'use strict'

const {type} = require('message-type')
const canvasApi = require('../canvasApi')
const Promise = require('bluebird')
const cl = require('../azureStorage')
const config = require('../server/init/configuration')
const log = require('../server/init/logging')
require('colors')

const csvVol = config.secure.azure.csvBlobName
const msgVol = config.secure.azure.msgBlobName
const csvDir = config.secure.localFile.csvDir
const lmsDatabase = config.secure.azure.databaseName
const lmsCollection = config.secure.azure.collectionName

function _createCsvFile (msg, sisCourseCode, enrollmentsArray, timeStamp) {
  let header = 'course_id,user_id,role,status\n'
  let msgtype = msg._desc.userType
  let csvFileName = 'enrollments.' + msgtype + '.' + sisCourseCode + '.' + timeStamp + '.csv'
  let msgFileName = 'enrollments.' + msgtype + '.' + sisCourseCode + '.' + timeStamp + '.msg'
  let csvString = ''
  let msgUsers = new Set([])
  let canvasUsers = new Set([])
  let canvasUserArray = []
  let csvArray = []
  let csvDarray = []

  log.info('Number of users recived from canvas: ' + enrollmentsArray.length)
  log.info('Number of users recieved from message: ' + msg.member.length)

  if (msg.member && msg.member.length > 0) {
    msgUsers = new Set(msg.member)
  }
  canvasUserArray = enrollmentsArray.map(canvasUser => canvasUser.sis_user_id)

  canvasUserArray.forEach(item => log.info('Debug canvas userid: ' + item))
  msg.member.forEach(item => log.info('debug msg userid: ' + item))

  if (canvasUserArray && canvasUserArray.length > 0) {
    canvasUsers = new Set(canvasUserArray)
  }
  let activateSet = new Set([...msgUsers].filter(user => !canvasUsers.has(user)))
  let deactivateSet = new Set([...canvasUsers].filter(user => !msgUsers.has(user)))
  let counter = 0
  csvArray = [...activateSet].map(user => {
    counter += 1
    log.info('[' + counter + '] ' + 'User: ' + user + ' will be enrolled to course: ' + sisCourseCode)
    return {
      course_id: sisCourseCode,
      user_id: user,
      role: msgtype,
      status: 'active'
    }
  })
  counter = 0
  csvDarray = [...deactivateSet].map(user => {
    counter += 1
    log.info('[' + counter + '] ' + 'User: ' + user + ' should be unrolled from course ' + sisCourseCode + ' ,no action taken for now: ')
    return {
      course_id: sisCourseCode,
      user_id: user,
      role: msgtype,
      status: 'deactivate'
    }
  })
  csvDarray = [] // Just to shut up the linter....
  log.info(csvDarray) // Just to shut up the linter....
  csvArray.forEach(csvRow => {
    csvString = csvString + `${csvRow.course_id},${csvRow.user_id},${csvRow.role},${csvRow.status}\n`
  })
  let csvData = header + csvString
  log.info('\nGoing to open file: ' + csvFileName + ' ' + msgFileName)

  if (csvString === '') {
    csvFileName = csvFileName + '.EMPTY'
  }

  return cl.cloudStoreTextToFile(csvFileName, csvVol, csvData)
  .then(result => { log.info(result); return cl.cloudStoreTextToFile(msgFileName, msgVol, JSON.stringify(msg, null, 4)) })
  .then(() => cl.cloudgetFile(csvFileName, csvVol, csvDir))
  .then(result => { log.info(result); let fileName = csvDir + result.name; return fileName })
  .catch(error => { log.error(error); return Promise.reject(error) })
}

function _parseKeyStudent (key) {
  // ladok2.kurser.DM.2517.registrerade_20162.1
  let course = null
  let termin = null
  let year = null
  let ladok = null
  let myRe = /^(\w+).(\w+).(\w+).(\w+).(\w+)_(\d\d)(\d\d)(\d).(\d+)/g
  let myArray = myRe.exec(key)
  if (myArray != null) {
    let courseInOne = 3
    let courseInTwo = 4
    let terminIn = 8
    let yearIn = 7
    let ladokIn = 9
    course = myArray[courseInOne] + myArray[courseInTwo]
    termin = myArray[terminIn] === 1 ? 'VT' : 'HT'
    year = myArray[yearIn]
    ladok = myArray[ladokIn]
    let sisCourseCode = course + termin + year + ladok
    return Promise.resolve(sisCourseCode)
  }
  return Promise.reject(Error('_parseKeyStudent, ' + key + ' could not be decoded'))
}

function _parseKeyTeacher (key) {
   // edu.courses.AE.AE2302.20162.1.teachers edu.courses.DD.DD1310.20162.1.assistants
  let course = null
  let termin = null
  let year = null
  let ladok = null
  let courseIn = 2
  let terminIn = 5
  let yearIn = 4
  let ladokIn = 6
  let myRe = /^edu.courses.(\w+).(\w+).(\d\d)(\d\d)(\d).(\d).(\w+)$/g
  let myArray = myRe.exec(key)
  if (myArray != null) {
    course = myArray[courseIn]
    termin = myArray[terminIn] === 1 ? 'VT' : 'HT'
    year = myArray[yearIn]
    ladok = myArray[ladokIn]
    let sisCourseCode = course + termin + year + ladok
    return Promise.resolve(sisCourseCode)
  }
  return Promise.reject(Error('_parseKeyTeacher, ' + key + ' could not be decoded'))
}

function _parseKey (key, msgtype) {
  if (msgtype === type.students) {
    return _parseKeyStudent(key)
  }
  if (msgtype === type.teachers || msgtype === type.assistants) {
    return _parseKeyTeacher(key)
  } else {
    return Promise.reject(Error('_parseKey unkown type, ' + msgtype))
  }
}

function _getEnrollmentsForCourse (canvasCourseId, msgtype) {
  let enrollType = ''
  switch (msgtype) {
    case type.students:
      enrollType = 'StudentEnrollment'
      break
    case type.teachers:
      enrollType = 'TeacherEnrollment'
      break
    case type.assistants:
      enrollType = 'TaEnrollment'
      break
    default:
      log.warn('enrollment type not defined....')
      return Promise.reject(Error('_getEnrollmentsForCourse, Invalid message type: ' + msgtype))
  }
  return canvasApi.getEnrollmentList(canvasCourseId, enrollType)
}

function _sendCsvIfNotEmpty (fileName) {
  if (fileName.split('.')[6] === 'EMPTY') {
    log.info('CSV file is empty, will not be sent to canvas, skipping....')
    return
  } else {
    return canvasApi.sendCsvFile(fileName)
  }
}

function _generateReport (csvTicket, sisCourseCode, timeStamp, document, timeInMinutes) {
  let milsecTime = timeInMinutes * 60000
  let documentId = sisCourseCode + '.' + timeStamp
  document['id'] = documentId
  if (!csvTicket) {
    // No need to wait for canvas, no file was sent
    document['csvTicket'] = 'Empty'
    document['csvReport'] = 'Empty'
    console.info(JSON.stringify(document, null, 4))
    let collectionUrl = `dbs/${lmsDatabase}/colls/${lmsCollection}`
    return cl.cloudCreateDocument(document, collectionUrl)
  } else {
    setTimeout(_createReport.bind(null, sisCourseCode, timeStamp, document), milsecTime)
    return Promise.resolve('Done')
  }
}

function _createReport (sisCourseCode, timeStamp, document) {
  let tmp = JSON.parse(document.csvTicket)
  document['csvTicket'] = tmp
  return canvasApi.getCsvFileStatus(tmp.id).then(result => {
    document['csvReport'] = JSON.parse(result)
    console.info(JSON.stringify(document, null, 4))
    let collectionUrl = `dbs/${lmsDatabase}/colls/${lmsCollection}`
    return cl.cloudCreateDocument(document, collectionUrl)
  })
}

function _handleError (err, sisCourseCode) {
  if (err.statusCode === 404) {
    log.info('Course does not exist in canvas, skipping, '.red + sisCourseCode.red)
    return Promise.resolve('Course does not exist in canvas')
  } else {
    return Promise.reject(Error(err))
  }
}

function _process (msg) {
  let sisCourseCode = ''
  let timeStamp = Date.now()
  let msgtype = msg._desc.userType
  let key = msg.ug1Name
  let document = {}

  return _parseKey(key, msgtype)
    .then(sisCode => {
      sisCourseCode = sisCode
      log.info(`In _process ${sisCourseCode}, processing for ${msgtype}`)
      document['sisCourseCode'] = sisCourseCode
      return canvasApi.findCourse(sisCourseCode)
    })
    .then(result => {
      let Result = JSON.parse(result)
      document['canvasCourse'] = Result
      return _getEnrollmentsForCourse(Result.id, msgtype)
    })
    .then(enrollmentsArray => {
      let tmpArray = enrollmentsArray.map(canvasUser => canvasUser.sis_user_id)
      document['usersInCanvas'] = tmpArray.sort()
      document['usersInmsg'] = msg.member.sort()
      return _createCsvFile(msg, sisCourseCode, enrollmentsArray, timeStamp)
    })
    .then(fileName => { document['fileName'] = fileName; return _sendCsvIfNotEmpty(fileName) })
    .then(csvTicket => {
      document['csvTicket'] = csvTicket
      let waitMinutes = 1
      return _generateReport(csvTicket, sisCourseCode, timeStamp, document, waitMinutes)
    })
    .catch(err => _handleError(err, sisCourseCode))
}

module.exports = function (msg, counter) {
  log.info('\nProcessing for msg..... ' + msg.ug1Name)
  var msgtype = msg._desc.userType
  if (msg._desc && (msgtype === type.students || msgtype === type.teachers || msgtype === type.assistants)) {
    return _process(msg)
  } else {
    log.error('\nThis is something else than students, teacher, assistant, we can probably wait with this until the students is handled', JSON.stringify(msg, null, 4))
    return Promise.resolve('Unknown flag: ' + msgtype)
  }
}
<|endoftext|>


<commit_before>var moment          = require('moment'),
    util            = require('util'),
    cfg             = require('nconf'),
    _s              = require('underscore.string'),
    labelUrlFormat  = cfg.get('app:labelUrlFormat'),
    pgnUrl          = cfg.get('app:pageUrlFormat'),
    pgnRegexp       = new RegExp(pgnUrl.replace(':page', '\\d+'));

/*
 * Builds post or static page URL.
 * It is assumed that 'this' is the current resonse
 */
function postUrl (post, absolute) {
    if (arguments.length < 2 && 'boolean' === typeof post) {
        absolute = post;
        post = this.locals.post;
    }

    var output = post.page ? '/:slug' : cfg.get('app:postUrlFormat'),
        tags = {
            ':year':   function () { return moment(post.publishedAt).format('YYYY'); },
            ':month':  function () { return moment(post.publishedAt).format('MM'); },
            ':day':    function () { return moment(post.publishedAt).format('DD'); },
            ':slug':   function () { return post.slug; },
            ':id':     function () { return post.id || post._id; }
        };

    // replace tags like :slug or :year with actual values
    output = output.replace(/(:[a-z]+)/g, function (match) {
        if (match in tags) {
            return tags[match]();
        }
    });

    return absolute ? cfg.get('url') + output : output;
}

function labelUrl (label, absolute) {
    return (absolute ? cfg.get('url') : '') +
        labelUrlFormat.replace(':label', encodeURIComponent(label));
}

/*
 * Builds pagination URL
 * It assumes that 'this' is current response
 */
function pageUrl (newer) {
    var pagination = this.locals.pagination,
        page = newer ? pagination.newer : pagination.older,
        ctx = this.locals.context.replace(pgnRegexp, '');

    return 1 === page ? ('' === ctx ? '/' : ctx)
        : ('/' === ctx ? '' : ctx) + pgnUrl.replace(':page', page);
}

/*
 * Formats given date. If the given date is a post then 'publishedAt' date is taken for formatting.
 */
function dateFormat (post, format) {
    if (arguments.length < 2) {
        if('string' === typeof post) {
            format = post;
            post = this.locals.post;
        } else {
            format = "YYYY-MM-DD";
        }
    }

    var date = util.isDate(post) || moment.isMoment(post) ? post : post && post.publishedAt;

    if (!date) return '';

    var mdate = moment(date);

    switch (format) {
        case 'timeago': return mdate.fromNow();
        case 'utc': return mdate.utc();
        case 'iso': return mdate.toISOString();
        default: return mdate.format(format);
    }
}

function labelsFormat (post, join) {
    if(2 > arguments.length && 'string' === typeof post) {
        join = post;
        post = this.locals.post;
    }

    return post.labels.join(join || ', ');
}

function metaTitle () {
    var post = this.locals.post,
        tokens = {
            ':blogtitle': function () { return cfg.get('app:title'); },
            ':posttitle': function () { return post.title; },
            ':authorname': function () { return post.author ? post.author.name : ''; }
        };

    return (post) ?
        cfg.get('app:postMetaTitleFormat').replace(/:[a-z]+/ig, function (token) {
            if (token in tokens) return tokens[token]();
        })
        : cfg.get('app:metaTitle') || cfg.get('app:title');
}

function metaDescription () {
    return cfg.get('app:metaDescription') || cfg.get('app:description');
}

function encode (text) {
    return encodeURIComponent(text);
}

function labelToClass (labels) {
    return (labels || []).map(function (label) {
        return 'tag-' + _s.slugify(label);
    });
}

function buildBodyClass () {
    // Main page template
    if ('/' === this.locals.context) return 'home-template';

    // Any pagination template
    if (+this.req.params.page) return 'archive-template';

    // Post or static page template (when res.locals.post is present)
    var post = this.locals.post;
    if (post) {
        var bodyClass = labelToClass(post.labels);
        bodyClass.push('post-template');

        // If post is actually a static page
        if (post.page) bodyClass.push('page');

        return bodyClass;
    }
}

function buildPostClass (post) {
    post = post || this.locals.post;

    var postClass = ['post'];

    if(post) {
        Array.prototype.push.apply(postClass, labelToClass(post.labels));
    }

    return postClass;
}

function copyright () {
    var app = this,
        tags = {
            ':year' : function () { return moment().format('YYYY'); },
            ':url'  : function () { return app.locals.url; },
            ':title': function () { return app.locals.title; }
        };
    return cfg.get('app:copyright').replace(/(:[a-z]+)/g, function (match) {
        if (match in tags) return tags[match]();
    });
}

function author () {
    return this.locals.post && this.locals.post.author;
}

function ifCheck (value, element, checkValue) {
    return element === checkValue ? value : undefined;
}

/*
 * Generates an asset URL.
 * It assumes that 'this' is current application
 */
function assets (asset) {
    return '/assets' + ('/' === asset[0] ? '' : '/') + asset + '?v=ad2e223fd';
}

function initRequest (req, res, next) {
    // Set default response local variables

    Object.defineProperties(res.locals, {
        context:    { enumerable: true, value: req.path },
        now:        { enumerable: true, value: moment() },
        $postClass: { enumerable: true, value: buildPostClass.bind(res) },
        $url:       { enumerable: true, value: postUrl.bind(res) },
        $pageUrl:   { enumerable: true, value: pageUrl.bind(res) },
        $labels:    { enumerable: true, value: labelsFormat.bind(res) },
        $date:      { enumerable: true, value: dateFormat.bind(res) },
        bodyClass:  { enumerable: true, get: buildBodyClass.bind(res) },
        postClass:  { enumerable: true, get: buildPostClass.bind(res) },
        metaTitle:  { enumerable: true, get: metaTitle.bind(res) },
        metaDescription: { enumerable: true, get: metaDescription.bind(res) },
        author:     { enumerable: true, get: author.bind(res) }
    });

    next();
}

function init (app) {
    // Set default application local variables as well as template helper functions
    Object.defineProperties(app.locals, {
        custom:      { enumerable: true, value: cfg.get('theme:custom') },
        title:       { enumerable: true, value: cfg.get('app:title') },
        description: { enumerable: true, value: cfg.get('app:description') },
        url:         { enumerable: true, value: cfg.get('url') },
        cover:       { enumerable: true, value: cfg.get('theme:cover') },
        logo:        { enumerable: true, value: cfg.get('theme:logo') },
        copyright:   { enumerable: true, get: copyright.bind(app) },
        $encode:     { enumerable: true, value: encode },
        $assets:     { enumerable: true, value: assets.bind(app) },
        $if:         { enumerable: true, value: ifCheck },
        $labelUrl:   { enumerable: true, value: labelUrl },

        // Update application locals with view settings like debug or pretty formatting
        pretty:      { enumerable: true, value: cfg.get('view:pretty') },
        debug:       { enumerable: true, value: cfg.get('view:debug') }
    });

    // Initialize reqest / response specific variables
    app.use(initRequest);
}

module.exports = init;<commit_msg>Panda version added to template locales<commit_after>var moment          = require('moment'),
    util            = require('util'),
    cfg             = require('nconf'),
    _s              = require('underscore.string'),
    labelUrlFormat  = cfg.get('app:labelUrlFormat'),
    pgnUrl          = cfg.get('app:pageUrlFormat'),
    pgnRegexp       = new RegExp(pgnUrl.replace(':page', '\\d+'));

/*
 * Builds post or static page URL.
 * It is assumed that 'this' is the current resonse
 */
function postUrl (post, absolute) {
    if (arguments.length < 2 && 'boolean' === typeof post) {
        absolute = post;
        post = this.locals.post;
    }

    var output = post.page ? '/:slug' : cfg.get('app:postUrlFormat'),
        tags = {
            ':year':   function () { return moment(post.publishedAt).format('YYYY'); },
            ':month':  function () { return moment(post.publishedAt).format('MM'); },
            ':day':    function () { return moment(post.publishedAt).format('DD'); },
            ':slug':   function () { return post.slug; },
            ':id':     function () { return post.id || post._id; }
        };

    // replace tags like :slug or :year with actual values
    output = output.replace(/(:[a-z]+)/g, function (match) {
        if (match in tags) {
            return tags[match]();
        }
    });

    return absolute ? cfg.get('url') + output : output;
}

function labelUrl (label, absolute) {
    return (absolute ? cfg.get('url') : '') +
        labelUrlFormat.replace(':label', encodeURIComponent(label));
}

/*
 * Builds pagination URL
 * It assumes that 'this' is current response
 */
function pageUrl (newer) {
    var pagination = this.locals.pagination,
        page = newer ? pagination.newer : pagination.older,
        ctx = this.locals.context.replace(pgnRegexp, '');

    return 1 === page ? ('' === ctx ? '/' : ctx)
        : ('/' === ctx ? '' : ctx) + pgnUrl.replace(':page', page);
}

/*
 * Formats given date. If the given date is a post then 'publishedAt' date is taken for formatting.
 */
function dateFormat (post, format) {
    if (arguments.length < 2) {
        if('string' === typeof post) {
            format = post;
            post = this.locals.post;
        } else {
            format = "YYYY-MM-DD";
        }
    }

    var date = util.isDate(post) || moment.isMoment(post) ? post : post && post.publishedAt;

    if (!date) return '';

    var mdate = moment(date);

    switch (format) {
        case 'timeago': return mdate.fromNow();
        case 'utc': return mdate.utc();
        case 'iso': return mdate.toISOString();
        default: return mdate.format(format);
    }
}

function labelsFormat (post, join) {
    if(2 > arguments.length && 'string' === typeof post) {
        join = post;
        post = this.locals.post;
    }

    return post.labels.join(join || ', ');
}

function metaTitle () {
    var post = this.locals.post,
        tokens = {
            ':blogtitle': function () { return cfg.get('app:title'); },
            ':posttitle': function () { return post.title; },
            ':authorname': function () { return post.author ? post.author.name : ''; }
        };

    return (post) ?
        cfg.get('app:postMetaTitleFormat').replace(/:[a-z]+/ig, function (token) {
            if (token in tokens) return tokens[token]();
        })
        : cfg.get('app:metaTitle') || cfg.get('app:title');
}

function metaDescription () {
    return cfg.get('app:metaDescription') || cfg.get('app:description');
}

function encode (text) {
    return encodeURIComponent(text);
}

function labelToClass (labels) {
    return (labels || []).map(function (label) {
        return 'tag-' + _s.slugify(label);
    });
}

function buildBodyClass () {
    // Main page template
    if ('/' === this.locals.context) return 'home-template';

    // Any pagination template
    if (+this.req.params.page) return 'archive-template';

    // Post or static page template (when res.locals.post is present)
    var post = this.locals.post;
    if (post) {
        var bodyClass = labelToClass(post.labels);
        bodyClass.push('post-template');

        // If post is actually a static page
        if (post.page) bodyClass.push('page');

        return bodyClass;
    }
}

function buildPostClass (post) {
    post = post || this.locals.post;

    var postClass = ['post'];

    if(post) {
        Array.prototype.push.apply(postClass, labelToClass(post.labels));
    }

    return postClass;
}

function copyright () {
    var app = this,
        tags = {
            ':year' : function () { return moment().format('YYYY'); },
            ':url'  : function () { return app.locals.url; },
            ':title': function () { return app.locals.title; }
        };
    return cfg.get('app:copyright').replace(/(:[a-z]+)/g, function (match) {
        if (match in tags) return tags[match]();
    });
}

function author () {
    return this.locals.post && this.locals.post.author;
}

function ifCheck (value, element, checkValue) {
    return element === checkValue ? value : undefined;
}

/*
 * Generates an asset URL.
 * It assumes that 'this' is current application
 */
function assets (asset) {
    return '/assets' + ('/' === asset[0] ? '' : '/') + asset + '?v=ad2e223fd';
}

function initRequest (req, res, next) {
    // Set default response local variables

    Object.defineProperties(res.locals, {
        context:    { enumerable: true, value: req.path },
        now:        { enumerable: true, value: moment() },
        $postClass: { enumerable: true, value: buildPostClass.bind(res) },
        $url:       { enumerable: true, value: postUrl.bind(res) },
        $pageUrl:   { enumerable: true, value: pageUrl.bind(res) },
        $labels:    { enumerable: true, value: labelsFormat.bind(res) },
        $date:      { enumerable: true, value: dateFormat.bind(res) },
        bodyClass:  { enumerable: true, get: buildBodyClass.bind(res) },
        postClass:  { enumerable: true, get: buildPostClass.bind(res) },
        metaTitle:  { enumerable: true, get: metaTitle.bind(res) },
        metaDescription: { enumerable: true, get: metaDescription.bind(res) },
        author:     { enumerable: true, get: author.bind(res) }
    });

    next();
}

function init (app) {
    // Set default application local variables as well as template helper functions
    Object.defineProperties(app.locals, {
        custom:      { enumerable: true, value: cfg.get('theme:custom') },
        title:       { enumerable: true, value: cfg.get('app:title') },
        description: { enumerable: true, value: cfg.get('app:description') },
        url:         { enumerable: true, value: cfg.get('url') },
        cover:       { enumerable: true, value: cfg.get('theme:cover') },
        logo:        { enumerable: true, value: cfg.get('theme:logo') },
        version:     { enumerable: true, value: module.exports.version },
        copyright:   { enumerable: true, get: copyright.bind(app) },
        $encode:     { enumerable: true, value: encode },
        $assets:     { enumerable: true, value: assets.bind(app) },
        $if:         { enumerable: true, value: ifCheck },
        $labelUrl:   { enumerable: true, value: labelUrl },

        // Update application locals with view settings like debug or pretty formatting
        pretty:      { enumerable: true, value: cfg.get('view:pretty') },
        debug:       { enumerable: true, value: cfg.get('view:debug') }
    });

    // Initialize reqest / response specific variables
    app.use(initRequest);
}

module.exports = init;

require('pkginfo')(module, 'version');<|endoftext|>


<commit_before>//initialize the map
var map = L.map('map').setView([42.3600825, -71.0588801], 12);

//Create baselayer - tiles
var backgroundMap = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
	attribution: 'OpenStreetMapcontributors, CC-BY-SA',
	maxZoom: 19
});

var secondLayer = L.tileLayer(' http://tile.stamen.com/toner/{z}/{x}/{y}.png', {
	maxZoom: 19
});

var layers = {
	"basic" : backgroundMap,
	"extra" : secondLayer
}

backgroundMap.addTo(map);

// map.layers = [backgroundMap, secondLayer]

L.control.layers(layers).addTo(map);

//Add markers
var brewery = L.marker([42.346868, -71.034396]).addTo(map);
brewery.addTo(map);

var aquarium = L.marker([42.359116, -71.049592]);
aquarium.addTo(map);

var hotel = L.marker([42.351340, -71.040495]);
hotel.addTo(map);

var harvard = L.marker([42.376979, -71.116617]);
harvard.addTo(map);

//Add pop-ups
// var popup = "The Harpoon Brewery.";
// brewery.bindPopup(popup);

var popup1 = "Do you sleep in the SeaPort Hotel?";
hotel.bindPopup(popup1)

var popup2 = "The New England Aquarium.";
aquarium.bindPopup(popup2);

var popup3 = "The Harvard University.";
harvard.bindPopup(popup3);

//add a circle
var circle = L.circle([42.359116, -71.049592], 4500, {
	color: 'red',
	fillColor: '#f03',
	fillOpacity: 0.5
}).addTo(map);

//add a polygon
var polygon = L.polygon([
	[42.346868, -71.034396],
	[42.351340, -71.040495],
	[42.359116, -71.049592],
	[42.376979, -71.116617]
		]).addTo(map);

// Create a marker first
// var bigfootIcon = {
// 		radius: 8,
// 		fillColor: "#ff7800",
// 		color: "#000",
// 		weight: 1,
// 		opacity: 1,
// 		fillOpacity: 0.8
// };

var bigfootIcon = L.icon({
    iconUrl: '../big_foot_orange.png',
    iconSize:     [15, 25], // size of the icon
});

//create the geojson layer
var geojson = L.geoJson(null,{
pointToLayer: function (geoJsonPoint, latlng) {
	return L.marker(latlng, {icon: bigfootIcon});
}
}).addTo(map);

// // new Http Request
// var xhttp = new XMLHttpRequest();

// // set the request method: get the data
// xhttp.open('GET', encodeURI("All_BFRO_Reports_points.geojson" ));

// //specify what must be done with the geojson data to the layer when request is succesfull
// xhttp.onload = function() {
// 	if (xhttp.readyState === 4) {
// 			// add the json data to the geojson layer we created before!
// 			geojson.addData(JSON.parse(xhttp.responseText));
// 		} else {
// 			alert('Request failed.  Returned status of ' + xhttp.readyState );
// 		}
// };

// // send the request
// xhttp.send();

//Jquery method
$.getJSON("../../data/All_BFRO_Reports_points.geojson", function(data) {
geojson.addData(data);
});

var popup = L.popup();

function onMapClick(e) {
    popup
        .setLatLng(e.latlng)
        .setContent("You clicked the map at " + e.latlng.toString())
        .openOn(map);
}

map.on('click', onMapClick);<commit_msg>test http request on github<commit_after>//initialize the map
var map = L.map('map').setView([42.3600825, -71.0588801], 12);

//Create baselayer - tiles
var backgroundMap = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
	attribution: 'OpenStreetMapcontributors, CC-BY-SA',
	maxZoom: 19
});

var secondLayer = L.tileLayer(' http://tile.stamen.com/toner/{z}/{x}/{y}.png', {
	maxZoom: 19
});

var layers = {
	"basic" : backgroundMap,
	"extra" : secondLayer
}

backgroundMap.addTo(map);

// map.layers = [backgroundMap, secondLayer]

L.control.layers(layers).addTo(map);

//Add markers
var brewery = L.marker([42.346868, -71.034396]).addTo(map);
brewery.addTo(map);

var aquarium = L.marker([42.359116, -71.049592]);
aquarium.addTo(map);

var hotel = L.marker([42.351340, -71.040495]);
hotel.addTo(map);

var harvard = L.marker([42.376979, -71.116617]);
harvard.addTo(map);

//Add pop-ups
// var popup = "The Harpoon Brewery.";
// brewery.bindPopup(popup);

var popup1 = "Do you sleep in the SeaPort Hotel?";
hotel.bindPopup(popup1)

var popup2 = "The New England Aquarium.";
aquarium.bindPopup(popup2);

var popup3 = "The Harvard University.";
harvard.bindPopup(popup3);

//add a circle
var circle = L.circle([42.359116, -71.049592], 4500, {
	color: 'red',
	fillColor: '#f03',
	fillOpacity: 0.5
}).addTo(map);

//add a polygon
var polygon = L.polygon([
	[42.346868, -71.034396],
	[42.351340, -71.040495],
	[42.359116, -71.049592],
	[42.376979, -71.116617]
		]).addTo(map);

// Create a marker first
// var bigfootIcon = {
// 		radius: 8,
// 		fillColor: "#ff7800",
// 		color: "#000",
// 		weight: 1,
// 		opacity: 1,
// 		fillOpacity: 0.8
// };

var bigfootIcon = L.icon({
    iconUrl: '../big_foot_orange.png',
    iconSize:     [15, 25], // size of the icon
});

//create the geojson layer
var geojson = L.geoJson(null,{
pointToLayer: function (geoJsonPoint, latlng) {
	return L.marker(latlng, {icon: bigfootIcon});
}
}).addTo(map);

// new Http Request
var xhttp = new XMLHttpRequest();

// set the request method: get the data
xhttp.open('GET', encodeURI("All_BFRO_Reports_points.geojson" ));

//specify what must be done with the geojson data to the layer when request is succesfull
xhttp.onload = function() {
	if (xhttp.readyState === 4) {
			// add the json data to the geojson layer we created before!
			geojson.addData(JSON.parse(xhttp.responseText));
		} else {
			alert('Request failed.  Returned status of ' + xhttp.readyState );
		}
};

// // send the request
// xhttp.send();

//Jquery method
// $.getJSON("../../data/All_BFRO_Reports_points.geojson", function(data) {
// geojson.addData(data);
// });

var popup = L.popup();

function onMapClick(e) {
    popup
        .setLatLng(e.latlng)
        .setContent("You clicked the map at " + e.latlng.toString())
        .openOn(map);
}

map.on('click', onMapClick);<|endoftext|>


<commit_before>import {get} from "../common"

export class GetImages {
    constructor(images, entries) {
        this.images = images
        this.imageEntries = Object.values(this.images)
        this.entries = entries
        this.counter = 0
    }

    init() {
        if (this.entries.length > 0) {
            if (this.entries[0].hasOwnProperty('url')) {
                return this.getImageUrlEntry()
            } else {
                return this.getImageZipEntry()
            }
        } else {
            return Promise.resolve()
        }
    }

    getImageZipEntry() {
        if (this.counter < this.imageEntries.length) {
            return new Promise(resolve => {
                const fc = this.entries.find(entry => entry.filename === this.imageEntries[
                    this.counter
                ].image).content
                this.imageEntries[this.counter]['file'] = new window.Blob(
                    [fc],
                    {type: this.imageEntries[this.counter].file_type}
                )
                this.counter++
                this.getImageZipEntry().then(() => {
                    resolve()
                })
            })
        } else {
            return Promise.resolve()
        }
    }

    getImageUrlEntry() {
        if (this.counter < this.imageEntries.length) {
            const getUrl = this.entries.find(
                entry => entry.filename === this.imageEntries[this.counter].image.split('/').pop()
            ).url
            return get(getUrl).then(
                response => response.blob()
            ).then(
                blob => {
                    // const mimeString = this.imageEntries[this.counter].file_type
                    // const dataView = new DataView(blob)
                    // const newBlob = new window.Blob([dataView], {type: mimeString})
                    // this.imageEntries[this.counter]['file'] = newBlob
                    this.imageEntries[this.counter]['file'] = blob
                    this.counter++
                    return this.getImageUrlEntry()
                }
            )
        } else {
            return Promise.resolve()
        }
    }
}
<commit_msg>fix copying of documents with images<commit_after>import {get} from "../common"

export class GetImages {
    constructor(images, entries) {
        this.images = images
        this.imageEntries = Object.values(this.images)
        this.entries = entries
        this.counter = 0
    }

    init() {
        if (this.entries.length > 0) {
            if (this.entries[0].hasOwnProperty('url')) {
                return this.getImageUrlEntry()
            } else {
                return this.getImageZipEntry()
            }
        } else {
            return Promise.resolve()
        }
    }

    getImageZipEntry() {
        if (this.counter < this.imageEntries.length) {
            return new Promise(resolve => {
                const fc = this.entries.find(entry => entry.filename === this.imageEntries[
                    this.counter
                ].image).content
                this.imageEntries[this.counter]['file'] = new window.Blob(
                    [fc],
                    {type: this.imageEntries[this.counter].file_type}
                )
                this.counter++
                this.getImageZipEntry().then(() => {
                    resolve()
                })
            })
        } else {
            return Promise.resolve()
        }
    }

    getImageUrlEntry() {
        if (this.counter < this.imageEntries.length) {
            const getUrl = this.entries.find(
                entry => entry.filename === `images/${this.imageEntries[this.counter].image.split('/').pop()}`
            ).url
            return get(getUrl).then(
                response => response.blob()
            ).then(
                blob => {
                    // const mimeString = this.imageEntries[this.counter].file_type
                    // const dataView = new DataView(blob)
                    // const newBlob = new window.Blob([dataView], {type: mimeString})
                    // this.imageEntries[this.counter]['file'] = newBlob
                    this.imageEntries[this.counter]['file'] = blob
                    this.counter++
                    return this.getImageUrlEntry()
                }
            )
        } else {
            return Promise.resolve()
        }
    }
}
<|endoftext|>


<commit_before>import { AKTUALNI_ROK } from '../../constants';
import { narozeniToStr, reverseSortDirType, SortDirTypes } from '../../Util';
import {
  csStringSortMethod,
  narozeniSortMethod,
  prijmeniJmenoNarozeniSortMethod
} from '../../entities/ucastnici/ucastniciReducer';
import { predepsaneStartovneCommon, provedenePlatby } from '../platby';

export const initialState = {
  fetching: false,
  kategorieVykonuFilter: '',
  textFilter: '',
  sortColumn: undefined,
  sortDir: SortDirTypes.NONE
};

const prihlaseniReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_UCASTNICI_REQUEST':
      return { ...state, fetching: true };
    case 'FETCH_UCASTNICI_SUCCESS':
    case 'FETCH_UCASTNICI_ERROR':
      return { ...state, fetching: false };
    case 'PRIHLASENI_KATEGORIE_VYKONU_FILTER_CHANGE':
      if (state.kategorieVykonuFilter === action.typKategorie) {
        return { ...state, kategorieVykonuFilter: '' };
      }
      return { ...state, kategorieVykonuFilter: action.typKategorie };
    case 'PRIHLASENI_TEXT_FILTER_CHANGE':
      return { ...state, textFilter: action.textFilter.toLowerCase() };
    case 'PRIHLASENI_SORT_DIR_CHANGE':
      if (state.sortColumn !== action.sortColumn) {
        return { ...state, sortColumn: action.sortColumn, sortDir: SortDirTypes.ASC };
      }
      return { ...state, sortDir: reverseSortDirType(state.sortDir) };
    default:
      return state;
  }
};

export default prihlaseniReducer;

const expandedPrihlaska = (prihlaska, kategorie) => {
  if (!prihlaska) {
    return {};
  }

  const { datum, kategorie: kategorieId, startCislo, kod } = prihlaska;
  return { datum, kategorie: kategorie[kategorieId], startCislo, kod };
};

export const getPrihlaseniSorted = ({
  kategorie,
  rocniky,
  ucastnici,
  kategorieVykonuFilter,
  textFilter,
  sortColumn,
  sortDir
}) => {
  const rok = AKTUALNI_ROK;
  const result = [];
  ucastnici.allIds.forEach(id => {
    const ucastnik = ucastnici.byIds[id];
    if (ucastnik.roky[0] === rok) {
      const ucast = ucastnik[rok];
      const { udaje: { prijmeni, jmeno, narozeni, obec }, prihlaska, platby } = ucast;
      const expPrihlaska = expandedPrihlaska(prihlaska, kategorie);
      const predepsano = predepsaneStartovneCommon({ kategorie, prihlaska, rocniky, rok }).suma;
      const zaplaceno = provedenePlatby(platby).suma;

      if (
        prijmeni.toLowerCase().startsWith(textFilter) ||
        jmeno.toLowerCase().startsWith(textFilter)
      ) {
        if (
          kategorieVykonuFilter === '' ||
          !prihlaska.kategorie ||
          kategorieVykonuFilter === expPrihlaska.kategorie.typ
        ) {
          result.push({
            id,
            prijmeni,
            jmeno,
            narozeni,
            obec,
            ...expPrihlaska,
            predepsano,
            zaplaceno
          });
        }
      }
    }
  });

  const sortMethods = {
    prijmeni: (a, b) => csStringSortMethod(a.prijmeni, b.prijmeni),
    jmeno: (a, b) => csStringSortMethod(a.jmeno, b.jmeno),
    narozeni: (a, b) => narozeniSortMethod(a.narozeni, b.narozeni)
  };

  const sortMethod = sortMethods[sortColumn] || prijmeniJmenoNarozeniSortMethod;
  const sorted = result.sort(sortMethod);
  if (sortDir === SortDirTypes.DESC) {
    sorted.reverse();
  }

  return sorted.map(ucastnik => {
    const { narozeni, ...ostatek } = ucastnik;
    return { ...ostatek, narozeni: narozeniToStr(narozeni) };
  });
};
<commit_msg>Simplify getPrihlaseniSorted().<commit_after>import { AKTUALNI_ROK } from '../../constants';
import { narozeniToStr, reverseSortDirType, SortDirTypes } from '../../Util';
import {
  csStringSortMethod,
  narozeniSortMethod,
  prijmeniJmenoNarozeniSortMethod
} from '../../entities/ucastnici/ucastniciReducer';
import { predepsaneStartovneCommon, provedenePlatby } from '../platby';

export const initialState = {
  fetching: false,
  kategorieVykonuFilter: '',
  textFilter: '',
  sortColumn: undefined,
  sortDir: SortDirTypes.NONE
};

const prihlaseniReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_UCASTNICI_REQUEST':
      return { ...state, fetching: true };
    case 'FETCH_UCASTNICI_SUCCESS':
    case 'FETCH_UCASTNICI_ERROR':
      return { ...state, fetching: false };
    case 'PRIHLASENI_KATEGORIE_VYKONU_FILTER_CHANGE':
      if (state.kategorieVykonuFilter === action.typKategorie) {
        return { ...state, kategorieVykonuFilter: '' };
      }
      return { ...state, kategorieVykonuFilter: action.typKategorie };
    case 'PRIHLASENI_TEXT_FILTER_CHANGE':
      return { ...state, textFilter: action.textFilter.toLowerCase() };
    case 'PRIHLASENI_SORT_DIR_CHANGE':
      if (state.sortColumn !== action.sortColumn) {
        return { ...state, sortColumn: action.sortColumn, sortDir: SortDirTypes.ASC };
      }
      return { ...state, sortDir: reverseSortDirType(state.sortDir) };
    default:
      return state;
  }
};

export default prihlaseniReducer;

export const getPrihlaseniSorted = ({
  kategorie,
  rocniky,
  ucastnici,
  kategorieVykonuFilter,
  textFilter,
  sortColumn,
  sortDir
}) => {
  const rok = AKTUALNI_ROK;
  const result = [];
  ucastnici.allIds.forEach(id => {
    const ucastnik = ucastnici.byIds[id];
    if (ucastnik.roky[0] === rok) {
      const ucast = ucastnik[rok];
      const { udaje: { prijmeni, jmeno, narozeni, obec }, prihlaska, platby } = ucast;
      const { datum, kategorie: kategorieId, startCislo, kod } = prihlaska;
      const jednaKategorie = kategorie[kategorieId];
      const predepsano = predepsaneStartovneCommon({ kategorie, prihlaska, rocniky, rok }).suma;
      const zaplaceno = provedenePlatby(platby).suma;

      if (
        prijmeni.toLowerCase().startsWith(textFilter) ||
        jmeno.toLowerCase().startsWith(textFilter)
      ) {
        if (kategorieVykonuFilter === '' || kategorieVykonuFilter === jednaKategorie.typ) {
          result.push({
            id,
            prijmeni,
            jmeno,
            narozeni,
            obec,
            datum,
            kategorie: jednaKategorie,
            startCislo,
            kod,
            predepsano,
            zaplaceno
          });
        }
      }
    }
  });

  const sortMethods = {
    prijmeni: (a, b) => csStringSortMethod(a.prijmeni, b.prijmeni),
    jmeno: (a, b) => csStringSortMethod(a.jmeno, b.jmeno),
    narozeni: (a, b) => narozeniSortMethod(a.narozeni, b.narozeni)
  };

  const sortMethod = sortMethods[sortColumn] || prijmeniJmenoNarozeniSortMethod;
  const sorted = result.sort(sortMethod);
  if (sortDir === SortDirTypes.DESC) {
    sorted.reverse();
  }

  return sorted.map(ucastnik => {
    const { narozeni, ...ostatek } = ucastnik;
    return { ...ostatek, narozeni: narozeniToStr(narozeni) };
  });
};
<|endoftext|>


<commit_before>;(function ($, StateManager, window) {
    'use strict';

    var msPointerEnabled = window.navigator.msPointerEnabled,
        $body = $('body');

    /**
     * Shopware Search Plugin.
     *
     * The plugin controlling the search field behaviour in all possible states
     */
    $.plugin('search', {

        defaults: {

            /**
             * Class which will be added when the drop down was triggered
             *
             * @type {String}
             */
            activeCls: 'is--active',

            /**
             * Class which will be used for generating search results
             *
             * @type {String}
             */
            searchFieldSelector: '.main-search--field',

            /**
             * Selector for the search result list.
             *
             * @type {String}
             */
            resultsSelector: '.main-search--results',

            /**
             * Selector for the link in a result entry.
             *
             * @type {String}
             */
            resultLinkSelector: '.search-result--link',

            /**
             * Selector for a single result entry.
             *
             * @type {String}
             */
            resultItemSelector: '.result--item',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            loadingIndicatorSelector: '.form--ajax-loader',

            /**
             * Selector for the main header element.
             * On mobile viewport the header get an active class when the
             * search bar is opened for additional styling.
             *
             * @type {String}
             */
            headerSelector: '.header-main',

            /**
             * Gets added when the search bar is active on mobile viewport.
             * Handles additional styling.
             *
             * @type {String}
             */
            activeHeaderClass: 'is--active-searchfield',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            triggerSelector: '.entry--trigger',

            /**
             * The URL used for the search request.
             * This option has to be set or an error will be thrown.
             *
             * @type {String}
             */
            requestUrl: '',

            /**
             * Flag whether or not the keyboard navigation is enabled
             *
             * @type {Boolean}
             */
            keyBoardNavigation: true,

            /**
             * Whether or not the active class is set by default
             *
             * @type {String}
             */
            activeOnStart: false,

            /**
             * Minimum amount of characters needed to trigger the search request
             *
             * @type {Number}
             */
            minLength: 3,

            /**
             * Time in milliseconds to wait after each key down event before
             * before starting the search request.
             * If a key was pressed in this time, the last request will be aborted.
             *
             * @type {Number}
             */
            searchDelay: 250,

            /**
             * The speed of all animations.
             *
             * @type {String|Number}
             */
            animationSpeed: 200,

            /**
             * The kay mapping for navigation the search results via keyboard.
             *
             * @type {Object}
             */
            keyMap: {
                'UP': 38,
                'DOWN': 40,
                'ENTER': 13
            }
        },

        /**
         * Initializes the plugin
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            me.applyDataAttributes();

            /**
             * The URL to which the search term will send via AJAX
             *
             * @public
             * @property requestURL
             * @type {String}
             */
            me.requestURL = opts.requestUrl || $.controller.ajax_search;

            if (!me.requestURL) {
                throw new Error('Parameter "requestUrl" needs to be set.');
            }

            /**
             * The search field itself.
             *
             * @public
             * @property $searchfield
             * @type {jQuery}
             */
            me.$searchField = $el.find(opts.searchFieldSelector);

            /**
             * The list in which the top results will be shown
             *
             * @public
             * @property $results
             * @type {jQuery}
             */
            me.$results = $el.find(opts.resultsSelector);

            /**
             * The loading indicator thats inside the search
             *
             * @public
             * @property $loader
             * @type {jQuery}
             */
            me.$loader = $el.find(opts.loadingIndicatorSelector);

            /**
             * The button to toggle the search field on mobile viewport
             *
             * @public
             * @property $toggleSearchBtn
             * @type {jQuery}
             */
            me.$toggleSearchBtn = $el.find(opts.triggerSelector);

            /**
             * The shop header to add a new class after opening
             *
             * @public
             * @property $mainHeader
             * @type {jQuery}
             */
            me.$mainHeader = $(opts.headerSelector);

            /**
             * The last search term that was entered in the search field.
             *
             * @public
             * @property lastSearchTerm
             * @type {String}
             */
            me.lastSearchTerm = '';

            /**
             * Timeout ID of the key up event.
             * The timeout is used to buffer fast key events.
             *
             * @public
             * @property keyupTimeout
             * @type {Number}
             */
            me.keyupTimeout = 0;

            me.registerListeners();
        },

        /**
         * Registers all necessary events for the plugin.
         *
         * @public
         * @method registerListeners
         */
        registerListeners: function () {
            var me = this,
                opts = me.opts,
                $searchField = me.$searchField;

            me._on($searchField, 'keyup', $.proxy(me.onKeyUp, me));
            me._on($searchField, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$toggleSearchBtn, 'click', $.proxy(me.onClickSearchEntry, me));

            if (msPointerEnabled) {
                me.$results.on('click', opts.resultLinkSelector, function (event) {
                    window.location.href = $(event.currentTarget).attr('href');
                });
            }

            StateManager.registerListener({
                state: 'xs',
                enter: function () {
                    if (opts.activeOnStart) {
                        me.openMobileSearch();
                    }
                },
                exit: function () {
                    me.closeMobileSearch();
                }
            });
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @public
         * @method onKeyDown
         * @param {jQuery.Event} event
         */
        onKeyDown: function (event) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                keyCode = event.which,
                navKeyPressed = opts.keyBoardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            $.publish('plugin/search/onKeyDown', [ me, event ]);

            if (navKeyPressed && me.$results.hasClass(opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
                event.preventDefault();
                return false;
            }

            return true;
        },

        /**
         * Will be called when a key was released on the search field.
         *
         * @public
         * @method onKeyUp
         * @param {jQuery.Event} event
         */
        onKeyUp: function (event) {
            var me = this,
                opts = me.opts,
                term = me.$searchField.val() + '',
                timeout = me.keyupTimeout;

            $.publish('plugin/search/onKeyUp', [ me, event ]);

            if (timeout) {
                window.clearTimeout(timeout);
            }

            if (term.length < opts.minLength) {
                me.lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if (term === me.lastSearchTerm) {
                return;
            }

            me.keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), opts.searchDelay);
        },

        /**
         * Triggers an AJAX request with the given search term.
         *
         * @public
         * @method triggerSearchRequest
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            me.$loader.fadeIn(me.opts.animationSpeed);

            me.lastSearchTerm = $.trim(searchTerm);

            $.publish('plugin/search/onSearchRequest', [ me, searchTerm ]);

            $.ajax({
                'url': me.requestURL,
                'data': {
                    'sSearch': me.lastSearchTerm
                },
                'success': function (response) {
                    me.showResult(response);
                    $.publish('plugin/search/onSearchResponse', [ me, searchTerm, response ]);
                }
            });
        },

        /**
         * Clears the result list and appends the given (AJAX) response to it.
         *
         * @public
         * @method showResult
         * @param {String} response
         */
        showResult: function (response) {
            var me = this,
                opts = me.opts;

            me.$loader.fadeOut(opts.animationSpeed);
            me.$results.empty().html(response).addClass(opts.activeCls).show();

            if (!StateManager.isCurrentState('xs')) {
                $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));
            }

            picturefill();

            $.publish('plugin/search/onShowResult', me);
        },

        /**
         * Closes the result list and removes all its items.
         *
         * @public
         * @method closeResult
         */
        closeResult: function () {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).hide().empty();

            $.publish('plugin/search/onCloseResult', me);
        },

        /**
         * Called when the body was clicked after the search field went active.
         * Closes the search field and results.
         *
         * @public
         * @method onClickBody
         * @param {jQuery.Event} event
         */
        onClickBody: function (event) {
            var me = this,
                target = event.target,
                pluginEl = me.$el[0],
                resultsEl = me.$results[0];

            if (target === pluginEl || target === resultsEl || $.contains(pluginEl, target) || $.contains(resultsEl, target)) {
                return;
            }

            $body.off(me.getEventName('click touchstart'));

            me.closeMobileSearch();
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @public
         * @method onKeyboardNavigation
         * @param {Number} keyCode
         */
        onKeyboardNavigation: function (keyCode) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                $results = me.$results,
                activeClass = opts.activeCls,
                $selected = $results.find('.' + activeClass),
                $resultItems,
                $nextSibling,
                firstLast;

            $.publish('plugin/search/onKeyboardNavigation', [ me, keyCode ]);

            if (keyCode === keyMap.UP || keyCode === keyMap.DOWN) {
                $resultItems = $results.find(opts.resultItemSelector);
                firstLast = (keyCode === keyMap.DOWN) ? 'first' : 'last';

                if (!$selected.length) {
                    $resultItems[firstLast]().addClass(activeClass);
                    return;
                }

                $resultItems.removeClass(activeClass);

                $nextSibling = $selected[(keyCode === keyMap.DOWN) ? 'next' : 'prev']();

                if ($nextSibling.length) {
                    $nextSibling.addClass(activeClass);
                    return;
                }

                $selected.siblings()[firstLast]().addClass(activeClass);
            }

            if (keyCode === keyMap.ENTER) {
                if ($selected.length) {
                    window.location.href = $selected.find(opts.resultLinkSelector).attr('href');
                    return;
                }

                me.$parent.submit();
            }
        },

        /**
         * onClickSearchTrigger event for displaying and hiding
         * the search field
         *
         * @public
         * @method onClickSearchEntry
         * @param event
         */
        onClickSearchEntry: function (event) {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            $.publish('plugin/search/onClickSearchEntry', [ me, event ]);

            if (!StateManager.isCurrentState('xs')) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            $el.hasClass(opts.activeCls) ? me.closeMobileSearch() : me.openMobileSearch();
        },

        /**
         * Opens the mobile search bar and focuses it.
         *
         * @public
         * @method openMobileSearch
         */
        openMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));

            $el.addClass(activeCls);
            me.$toggleSearchBtn.addClass(activeCls);
            me.$mainHeader.addClass(opts.activeHeaderClass);

            me.$searchField.focus();

            $.publish('plugin/search/onOpenMobileSearch', [ me ]);
        },

        /**
         * Closes the mobile search bar and removes its focus.
         *
         * @public
         * @method closeMobileSearch
         */
        closeMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $el.removeClass(activeCls);
            me.$toggleSearchBtn.removeClass(activeCls);
            me.$mainHeader.removeClass(opts.activeHeaderClass);

            me.$searchField.blur();

            $.publish('plugin/search/onCloseMobileSearch', [ me ]);

            me.closeResult();
        },

        /**
         * Destroys the plugin and removes registered events.
         *
         * @public
         * @method destroy
         */
        destroy: function () {
            var me = this;

            me.closeMobileSearch();

            $body.off(me.getEventName('click touchstart'));

            me._destroy();
        }
    });
})(jQuery, StateManager, window);<commit_msg>SW-10941 - Use protocol relative URL for live search<commit_after>;(function ($, StateManager, window) {
    'use strict';

    var msPointerEnabled = window.navigator.msPointerEnabled,
        $body = $('body');

    /**
     * Shopware Search Plugin.
     *
     * The plugin controlling the search field behaviour in all possible states
     */
    $.plugin('search', {

        defaults: {

            /**
             * Class which will be added when the drop down was triggered
             *
             * @type {String}
             */
            activeCls: 'is--active',

            /**
             * Class which will be used for generating search results
             *
             * @type {String}
             */
            searchFieldSelector: '.main-search--field',

            /**
             * Selector for the search result list.
             *
             * @type {String}
             */
            resultsSelector: '.main-search--results',

            /**
             * Selector for the link in a result entry.
             *
             * @type {String}
             */
            resultLinkSelector: '.search-result--link',

            /**
             * Selector for a single result entry.
             *
             * @type {String}
             */
            resultItemSelector: '.result--item',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            loadingIndicatorSelector: '.form--ajax-loader',

            /**
             * Selector for the main header element.
             * On mobile viewport the header get an active class when the
             * search bar is opened for additional styling.
             *
             * @type {String}
             */
            headerSelector: '.header-main',

            /**
             * Gets added when the search bar is active on mobile viewport.
             * Handles additional styling.
             *
             * @type {String}
             */
            activeHeaderClass: 'is--active-searchfield',

            /**
             * Selector for the ajax loading indicator.
             *
             * @type {String}
             */
            triggerSelector: '.entry--trigger',

            /**
             * The URL used for the search request.
             * This option has to be set or an error will be thrown.
             *
             * @type {String}
             */
            requestUrl: '',

            /**
             * Flag whether or not the keyboard navigation is enabled
             *
             * @type {Boolean}
             */
            keyBoardNavigation: true,

            /**
             * Whether or not the active class is set by default
             *
             * @type {String}
             */
            activeOnStart: false,

            /**
             * Minimum amount of characters needed to trigger the search request
             *
             * @type {Number}
             */
            minLength: 3,

            /**
             * Time in milliseconds to wait after each key down event before
             * before starting the search request.
             * If a key was pressed in this time, the last request will be aborted.
             *
             * @type {Number}
             */
            searchDelay: 250,

            /**
             * The speed of all animations.
             *
             * @type {String|Number}
             */
            animationSpeed: 200,

            /**
             * The kay mapping for navigation the search results via keyboard.
             *
             * @type {Object}
             */
            keyMap: {
                'UP': 38,
                'DOWN': 40,
                'ENTER': 13
            }
        },

        /**
         * Initializes the plugin
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            me.applyDataAttributes();

            /**
             * The URL to which the search term will send via AJAX
             *
             * @public
             * @property requestURL
             * @type {String}
             */
            me.requestURL = opts.requestUrl || $.controller.ajax_search;

            if (!me.requestURL) {
                throw new Error('Parameter "requestUrl" needs to be set.');
            }

            /**
            * Converts the url to a protocol relative url, so we don't need to manually
            * check the used http protocol. See the example from paul irish to get an idea
            * how it should work:
            *    `http://www.paulirish.com/2010/the-protocol-relative-url/`
            *    `http://blog.httpwatch.com/2010/02/10/using-protocol-relative-urls-to-switch-between-http-and-https/`
            *
            * @param {String} url - the url which needs to be converted
            * @returns {String} converted string
            */
            var convertUrlToRelativeUrl = function(url) {
                url = url.replace('https:', '');
                url = url.replace('http:', '');

                return url;
            };

            me.requestURL = convertUrlToRelativeUrl(me.requestURL);

            /**
             * The search field itself.
             *
             * @public
             * @property $searchfield
             * @type {jQuery}
             */
            me.$searchField = $el.find(opts.searchFieldSelector);

            /**
             * The list in which the top results will be shown
             *
             * @public
             * @property $results
             * @type {jQuery}
             */
            me.$results = $el.find(opts.resultsSelector);

            /**
             * The loading indicator thats inside the search
             *
             * @public
             * @property $loader
             * @type {jQuery}
             */
            me.$loader = $el.find(opts.loadingIndicatorSelector);

            /**
             * The button to toggle the search field on mobile viewport
             *
             * @public
             * @property $toggleSearchBtn
             * @type {jQuery}
             */
            me.$toggleSearchBtn = $el.find(opts.triggerSelector);

            /**
             * The shop header to add a new class after opening
             *
             * @public
             * @property $mainHeader
             * @type {jQuery}
             */
            me.$mainHeader = $(opts.headerSelector);

            /**
             * The last search term that was entered in the search field.
             *
             * @public
             * @property lastSearchTerm
             * @type {String}
             */
            me.lastSearchTerm = '';

            /**
             * Timeout ID of the key up event.
             * The timeout is used to buffer fast key events.
             *
             * @public
             * @property keyupTimeout
             * @type {Number}
             */
            me.keyupTimeout = 0;

            me.registerListeners();
        },

        /**
         * Registers all necessary events for the plugin.
         *
         * @public
         * @method registerListeners
         */
        registerListeners: function () {
            var me = this,
                opts = me.opts,
                $searchField = me.$searchField;

            me._on($searchField, 'keyup', $.proxy(me.onKeyUp, me));
            me._on($searchField, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$toggleSearchBtn, 'click', $.proxy(me.onClickSearchEntry, me));

            if (msPointerEnabled) {
                me.$results.on('click', opts.resultLinkSelector, function (event) {
                    window.location.href = $(event.currentTarget).attr('href');
                });
            }

            StateManager.registerListener({
                state: 'xs',
                enter: function () {
                    if (opts.activeOnStart) {
                        me.openMobileSearch();
                    }
                },
                exit: function () {
                    me.closeMobileSearch();
                }
            });
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @public
         * @method onKeyDown
         * @param {jQuery.Event} event
         */
        onKeyDown: function (event) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                keyCode = event.which,
                navKeyPressed = opts.keyBoardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            $.publish('plugin/search/onKeyDown', [ me, event ]);

            if (navKeyPressed && me.$results.hasClass(opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
                event.preventDefault();
                return false;
            }

            return true;
        },

        /**
         * Will be called when a key was released on the search field.
         *
         * @public
         * @method onKeyUp
         * @param {jQuery.Event} event
         */
        onKeyUp: function (event) {
            var me = this,
                opts = me.opts,
                term = me.$searchField.val() + '',
                timeout = me.keyupTimeout;

            $.publish('plugin/search/onKeyUp', [ me, event ]);

            if (timeout) {
                window.clearTimeout(timeout);
            }

            if (term.length < opts.minLength) {
                me.lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if (term === me.lastSearchTerm) {
                return;
            }

            me.keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), opts.searchDelay);
        },

        /**
         * Triggers an AJAX request with the given search term.
         *
         * @public
         * @method triggerSearchRequest
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            me.$loader.fadeIn(me.opts.animationSpeed);

            me.lastSearchTerm = $.trim(searchTerm);

            $.publish('plugin/search/onSearchRequest', [ me, searchTerm ]);

            $.ajax({
                'url': me.requestURL,
                'data': {
                    'sSearch': me.lastSearchTerm
                },
                'success': function (response) {
                    me.showResult(response);
                    $.publish('plugin/search/onSearchResponse', [ me, searchTerm, response ]);
                }
            });
        },

        /**
         * Clears the result list and appends the given (AJAX) response to it.
         *
         * @public
         * @method showResult
         * @param {String} response
         */
        showResult: function (response) {
            var me = this,
                opts = me.opts;

            me.$loader.fadeOut(opts.animationSpeed);
            me.$results.empty().html(response).addClass(opts.activeCls).show();

            if (!StateManager.isCurrentState('xs')) {
                $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));
            }

            picturefill();

            $.publish('plugin/search/onShowResult', me);
        },

        /**
         * Closes the result list and removes all its items.
         *
         * @public
         * @method closeResult
         */
        closeResult: function () {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).hide().empty();

            $.publish('plugin/search/onCloseResult', me);
        },

        /**
         * Called when the body was clicked after the search field went active.
         * Closes the search field and results.
         *
         * @public
         * @method onClickBody
         * @param {jQuery.Event} event
         */
        onClickBody: function (event) {
            var me = this,
                target = event.target,
                pluginEl = me.$el[0],
                resultsEl = me.$results[0];

            if (target === pluginEl || target === resultsEl || $.contains(pluginEl, target) || $.contains(resultsEl, target)) {
                return;
            }

            $body.off(me.getEventName('click touchstart'));

            me.closeMobileSearch();
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @public
         * @method onKeyboardNavigation
         * @param {Number} keyCode
         */
        onKeyboardNavigation: function (keyCode) {
            var me = this,
                opts = me.opts,
                keyMap = opts.keyMap,
                $results = me.$results,
                activeClass = opts.activeCls,
                $selected = $results.find('.' + activeClass),
                $resultItems,
                $nextSibling,
                firstLast;

            $.publish('plugin/search/onKeyboardNavigation', [ me, keyCode ]);

            if (keyCode === keyMap.UP || keyCode === keyMap.DOWN) {
                $resultItems = $results.find(opts.resultItemSelector);
                firstLast = (keyCode === keyMap.DOWN) ? 'first' : 'last';

                if (!$selected.length) {
                    $resultItems[firstLast]().addClass(activeClass);
                    return;
                }

                $resultItems.removeClass(activeClass);

                $nextSibling = $selected[(keyCode === keyMap.DOWN) ? 'next' : 'prev']();

                if ($nextSibling.length) {
                    $nextSibling.addClass(activeClass);
                    return;
                }

                $selected.siblings()[firstLast]().addClass(activeClass);
            }

            if (keyCode === keyMap.ENTER) {
                if ($selected.length) {
                    window.location.href = $selected.find(opts.resultLinkSelector).attr('href');
                    return;
                }

                me.$parent.submit();
            }
        },

        /**
         * onClickSearchTrigger event for displaying and hiding
         * the search field
         *
         * @public
         * @method onClickSearchEntry
         * @param event
         */
        onClickSearchEntry: function (event) {
            var me = this,
                $el = me.$el,
                opts = me.opts;

            $.publish('plugin/search/onClickSearchEntry', [ me, event ]);

            if (!StateManager.isCurrentState('xs')) {
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            $el.hasClass(opts.activeCls) ? me.closeMobileSearch() : me.openMobileSearch();
        },

        /**
         * Opens the mobile search bar and focuses it.
         *
         * @public
         * @method openMobileSearch
         */
        openMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $body.on(me.getEventName('click touchstart'), $.proxy(me.onClickBody, me));

            $el.addClass(activeCls);
            me.$toggleSearchBtn.addClass(activeCls);
            me.$mainHeader.addClass(opts.activeHeaderClass);

            me.$searchField.focus();

            $.publish('plugin/search/onOpenMobileSearch', [ me ]);
        },

        /**
         * Closes the mobile search bar and removes its focus.
         *
         * @public
         * @method closeMobileSearch
         */
        closeMobileSearch: function () {
            var me = this,
                $el = me.$el,
                opts = me.opts,
                activeCls = opts.activeCls;

            $el.removeClass(activeCls);
            me.$toggleSearchBtn.removeClass(activeCls);
            me.$mainHeader.removeClass(opts.activeHeaderClass);

            me.$searchField.blur();

            $.publish('plugin/search/onCloseMobileSearch', [ me ]);

            me.closeResult();
        },

        /**
         * Destroys the plugin and removes registered events.
         *
         * @public
         * @method destroy
         */
        destroy: function () {
            var me = this;

            me.closeMobileSearch();

            $body.off(me.getEventName('click touchstart'));

            me._destroy();
        }
    });
})(jQuery, StateManager, window);
<|endoftext|>