<commit_before>/*global ajax401: false, prefix: false, updateAjaxURL: false, traveler: true,
FormLoader: false, moment: false*/
/*global previewColumn: false, referenceFormLinkColumn: false, aliasColumn: false,
activatedOnColumn: false, sDomClean: false, titleColumn: false, updatedOnColumn:
false, formColumn: false, sDomPage: false, fnAddFilterFoot: false, filterEvent:
false*/
/*eslint max-nested-callbacks: [2, 4]*/

function findById(a, id) {
  var i;
  for (i = 0; i < a.length; i += 1) {
    if (a[i]._id === id) {
      return a[i];
    }
  }
  return null;
}

function addForm(form, cb) {
  $.ajax({
    url: './discrepency-forms/',
    type: 'POST',
    contentType: 'application/json',
    dataType: 'json',
    data: JSON.stringify(form),
    processData: false,
  })
    .done(function(json) {
      $('#modal .modal-body').append(
        '
The selected form is active now.
'
      );
      cb(json);
    })
    .fail(function(jqXHR) {
      $('#modal .modal-body').append(
        '
Something was wrong: ' +
          jqXHR.responseText +
          '
'
      );
    });
}

function setActive(fid, cb) {
  $.ajax({
    url: './discrepency-forms/active',
    type: 'PUT',
    contentType: 'application/json',
    dataType: 'json',
    data: JSON.stringify({
      formId: fid,
    }),
    processData: false,
  })
    .done(function(json) {
      $('#modal .modal-body').append(
        '
The selected discrepency form is active now.
'
      );
      cb(json);
    })
    .fail(function(jqXHR) {
      $('#modal .modal-body').append(
        '
Something was wrong: ' +
          jqXHR.responseText +
          '
'
      );
    });
}

function initUsedForms(traveler, activeTable, usedTable) {
  var form;
  if (traveler.discrepencyForms && traveler.discrepencyForms.length === 1) {
    form = traveler.discrepencyForms[0];
  } else {
    form = findById(traveler.discrepencyForms, traveler.activeDiscrepancyForm);
  }
  if (form) {
    var active = {
      activatedOn: form.activatedOn.length
        ? form.activatedOn
        : [traveler.createdOn],
      _id: form._id,
      reference: form.reference || traveler.referenceForm,
    };
    activeTable.fnClearTable();
    activeTable.fnAddData(active);
  }
  var used = [];
  if (traveler.discrepencyForms.length > 1) {
    traveler.discrepencyForms.forEach(function(value) {
      if (value._id !== traveler.activeDiscrepancyForm) {
        value.activatedOn = value.activatedOn.length
          ? value.activatedOn
          : [traveler.createdOn];
        value.reference = value.reference || traveler.referenceForm;
        used.push(value);
      }
    });
  }
  usedTable.fnClearTable();
  usedTable.fnAddData(used);
  $('tr').removeClass('row-selected');
  $('#active-form tbody tr:first-child').addClass('row-selected');
}

$(function() {
  ajax401(prefix);

  updateAjaxURL(prefix);

  var activeColumns = [
    previewColumn,
    activatedOnColumn,
    referenceFormLinkColumn,
  ];
  var activeTable = $('#active-form').dataTable({
    aaData: [],
    bAutoWidth: true,
    aoColumns: activeColumns,
    sDom: sDomClean,
  });

  var usedColumns = activeColumns;

  fnAddFilterFoot('#used-forms', usedColumns);
  var usedTable = $('#used-forms').dataTable({
    aaData: [],
    bAutoWidth: true,
    aoColumns: usedColumns,
    sDom: sDomPage,
  });

  if (traveler.discrepencyForms && traveler.discrepencyForms.length > 0) {
    initUsedForms(traveler, activeTable, usedTable);
  }

  var availableColumns = [
    previewColumn,
    titleColumn,
    updatedOnColumn,
    formColumn,
  ];
  fnAddFilterFoot('#available-forms', availableColumns);
  var availableTable = $('#available-forms').dataTable({
    sAjaxSource: '/released-forms/discrepency/json',
    sAjaxDataProp: '',
    bProcessing: true,
    oLanguage: {
      sLoadingRecords: 'Please wait - loading data from the server ...',
    },
    aoColumns: availableColumns,
    aaSorting: [[2, 'desc']],
    sDom: sDomPage,
  });

  function loadForm(html) {
    FormLoader.setFormHTML(html);
    FormLoader.loadForm();
    FormLoader.bind();
    FormLoader.note();
  }

  FormLoader.setTravelerId(traveler._id);
  var form;
  if (traveler.forms.length === 1) {
    form = traveler.forms[0];
  } else {
    form = findById(traveler.forms, traveler.activeForm);
  }

  if (!form) {
    $('#message').append(
      '
<button class="close" data-dismiss="alert">x</button>HTTP request failed.
'
    );
    $(window).scrollTop($('#message div:last-child').offset().top - 40);
  }

  // discrepency on the top

  loadForm(form.html);

  // local cache of available forms
  var availableForms = {};

  // add forms to available forms object
  traveler.forms.forEach(function(f) {
    availableForms[f._id] = f;
  });

  filterEvent();

  $('tbody').on('click', 'a.preview', function preview() {
    var row = $(this).closest('tr');
    if (row.hasClass('row-selected')) {
      return;
    }
    $('#form').fadeTo('slow', 0.5);
    $('tr').removeClass('row-selected');
    row.addClass('row-selected');

    var fid = this.id;

    if (!availableForms.hasOwnProperty(fid)) {
      FormLoader.retrieveForm(fid, function(json) {
        $('#form').fadeTo('slow', 1);
        availableForms[fid] = json;
        loadForm(availableForms[fid].html);
      });
    } else {
      $('#form').fadeTo('slow', 1);
      loadForm(availableForms[fid].html);
    }
  });

  $('#use').click(function() {
    var selected = $('.row-selected');
    var fid = $('a.preview', selected).prop('id');
    var tid = selected.closest('table').prop('id');
    var newform;
    if (tid === 'active-form') {
      $('#modalLabel').html('Alert');
      $('#modal .modal-body').html('The selected form is currently in use.');
      $('#modal .modal-footer').html(
        '<button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
    } else if (tid === 'used-forms') {
      $('#modalLabel').html('Use the following used form');
      $('#modal .modal-body').html(
        '' +
          availableForms[fid].alias +
          ' last activated on ' +
          moment(
            availableForms[fid].activatedOn[
              availableForms[fid].activatedOn.length - 1
            ]
          ).format('YYYY-MM-DD HH:mm:ss')
      );
      $('#modal .modal-footer').html(
        '<button id="submit" class="btn btn-primary">Confirm</button><button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
      $('#submit').click(function() {
        $('#submit').prop('disabled', true);
        // set the active form id
        setActive(fid, function(json) {
          traveler = json;
          initUsedForms(json, activeTable, usedTable);
        });
      });
    } else {
      $('#modalLabel').html('Use the following selected form');
      $('#modal .modal-body').html(
        '' +
          availableForms[fid].title +
          ' updated on ' +
          moment(availableForms[fid].updatedOn).format('YYYY-MM-DD HH:mm:ss')
      );
      $('#modal .modal-footer').html(
        '<button id="submit" class="btn btn-primary">Confirm</button><button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
      $('#submit').click(function() {
        // add the new form to the traveler forms list and set it active
        $('#submit').prop('disabled', true);
        newform = {
          formId: availableForms[fid]._id,
        };
        addForm(newform, function(json) {
          traveler = json;
          initUsedForms(json, activeTable, usedTable);
        });
      });
    }
  });
});
<commit_msg>discrepancy preview ready<commit_after>/*global ajax401: false, prefix: false, updateAjaxURL: false, traveler: true,
FormLoader: false, moment: false*/
/*global previewColumn: false, referenceFormLinkColumn: false, aliasColumn: false,
activatedOnColumn: false, sDomClean: false, titleColumn: false, updatedOnColumn:
false, formColumn: false, sDomPage: false, fnAddFilterFoot: false, filterEvent:
false*/
/*eslint max-nested-callbacks: [2, 4]*/

function findById(a, id) {
  var i;
  for (i = 0; i < a.length; i += 1) {
    if (a[i]._id === id) {
      return a[i];
    }
  }
  return null;
}

function addForm(form, cb) {
  $.ajax({
    url: './discrepency-forms/',
    type: 'POST',
    contentType: 'application/json',
    dataType: 'json',
    data: JSON.stringify(form),
    processData: false,
  })
    .done(function(json) {
      $('#modal .modal-body').append(
        '
The selected form is active now.
'
      );
      cb(json);
    })
    .fail(function(jqXHR) {
      $('#modal .modal-body').append(
        '
Something was wrong: ' +
          jqXHR.responseText +
          '
'
      );
    });
}

function setActive(fid, cb) {
  $.ajax({
    url: './discrepency-forms/active',
    type: 'PUT',
    contentType: 'application/json',
    dataType: 'json',
    data: JSON.stringify({
      formId: fid,
    }),
    processData: false,
  })
    .done(function(json) {
      $('#modal .modal-body').append(
        '
The selected discrepency form is active now.
'
      );
      cb(json);
    })
    .fail(function(jqXHR) {
      $('#modal .modal-body').append(
        '
Something was wrong: ' +
          jqXHR.responseText +
          '
'
      );
    });
}

function initUsedForms(traveler, activeTable, usedTable) {
  var form;
  if (traveler.discrepencyForms && traveler.discrepencyForms.length === 1) {
    form = traveler.discrepencyForms[0];
  } else {
    form = findById(traveler.discrepencyForms, traveler.activeDiscrepancyForm);
  }
  if (form) {
    var active = {
      activatedOn: form.activatedOn.length
        ? form.activatedOn
        : [traveler.createdOn],
      _id: form._id,
      reference: form.reference || traveler.referenceForm,
    };
    activeTable.fnClearTable();
    activeTable.fnAddData(active);
  }
  var used = [];
  if (traveler.discrepencyForms.length > 1) {
    traveler.discrepencyForms.forEach(function(value) {
      if (value._id !== traveler.activeDiscrepancyForm) {
        value.activatedOn = value.activatedOn.length
          ? value.activatedOn
          : [traveler.createdOn];
        value.reference = value.reference || traveler.referenceForm;
        used.push(value);
      }
    });
  }
  usedTable.fnClearTable();
  usedTable.fnAddData(used);
  $('tr').removeClass('row-selected');
  $('#active-form tbody tr:first-child').addClass('row-selected');
}

function loadForm(html) {
  FormLoader.setFormHTML(html);
  FormLoader.loadForm();
  FormLoader.bind();
  FormLoader.note();
}

$(function() {
  ajax401(prefix);

  updateAjaxURL(prefix);

  var activeColumns = [
    previewColumn,
    activatedOnColumn,
    versionColumn,
    referenceFormLinkColumn,
  ];
  var activeTable = $('#active-form').dataTable({
    aaData: [],
    bAutoWidth: true,
    aoColumns: activeColumns,
    sDom: sDomClean,
  });

  var usedColumns = activeColumns;

  fnAddFilterFoot('#used-forms', usedColumns);
  var usedTable = $('#used-forms').dataTable({
    aaData: [],
    bAutoWidth: true,
    aoColumns: usedColumns,
    sDom: sDomPage,
  });

  if (traveler.discrepencyForms && traveler.discrepencyForms.length > 0) {
    initUsedForms(traveler, activeTable, usedTable);
  }

  var availableColumns = [
    previewColumn,
    titleColumn,
    updatedOnColumn,
    formColumn,
  ];
  fnAddFilterFoot('#available-forms', availableColumns);
  var availableTable = $('#available-forms').dataTable({
    sAjaxSource: '/released-forms/discrepency/json',
    sAjaxDataProp: '',
    bProcessing: true,
    oLanguage: {
      sLoadingRecords: 'Please wait - loading data from the server ...',
    },
    aoColumns: availableColumns,
    aaSorting: [[2, 'desc']],
    sDom: sDomPage,
  });

  var discrepencyLegend =
    '
<legend>Discrepancy</legend>
';
  var travelerLegend =
    '
<legend>Traveler</legend>
';

  FormLoader.setTravelerId(traveler._id);
  var form;
  if (traveler.forms.length === 1) {
    form = traveler.forms[0];
  } else {
    form = findById(traveler.forms, traveler.activeForm);
  }

  var discrepencyForm;
  if (traveler.activeDiscrepancyForm) {
    discrepencyForm = findById(
      traveler.discrepencyForms,
      traveler.activeDiscrepancyForm
    );
  }

  if (!form) {
    $('#message').append(
      '
<button class="close" data-dismiss="alert">x</button>HTTP request failed.
'
    );
    $(window).scrollTop($('#message div:last-child').offset().top - 40);
  } else {
    let html = form.html;
    // discrepency on the top
    if (discrepencyForm) {
      html =
        discrepencyLegend + discrepencyForm.html + travelerLegend + form.html;
    }
    loadForm(html);
  }

  // local cache of available forms
  var availableForms = {};

  // add forms to available forms object
  traveler.forms.forEach(function(f) {
    availableForms[f._id] = f;
  });

  filterEvent();

  $('tbody').on('click', 'a.preview', function preview() {
    var row = $(this).closest('tr');
    if (row.hasClass('row-selected')) {
      return;
    }
    $('#form').fadeTo('slow', 0.5);
    $('tr').removeClass('row-selected');
    row.addClass('row-selected');

    var fid = this.id;

    if (!availableForms.hasOwnProperty(fid)) {
      FormLoader.retrieveForm(fid, function(json) {
        $('#form').fadeTo('slow', 1);
        availableForms[fid] = json;
        let html =
          discrepencyLegend +
          availableForms[fid].html +
          travelerLegend +
          form.html;
        loadForm(html);
      });
    } else {
      $('#form').fadeTo('slow', 1);
      let html =
        discrepencyLegend +
        availableForms[fid].html +
        travelerLegend +
        form.html;
      loadForm(availableForms[fid].html);
    }
  });

  $('#use').click(function() {
    var selected = $('.row-selected');
    var fid = $('a.preview', selected).prop('id');
    var tid = selected.closest('table').prop('id');
    var newform;
    if (tid === 'active-form') {
      $('#modalLabel').html('Alert');
      $('#modal .modal-body').html('The selected form is currently in use.');
      $('#modal .modal-footer').html(
        '<button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
    } else if (tid === 'used-forms') {
      $('#modalLabel').html('Use the following used form');
      $('#modal .modal-body').html(
        '' +
          availableForms[fid].alias +
          ' last activated on ' +
          moment(
            availableForms[fid].activatedOn[
              availableForms[fid].activatedOn.length - 1
            ]
          ).format('YYYY-MM-DD HH:mm:ss')
      );
      $('#modal .modal-footer').html(
        '<button id="submit" class="btn btn-primary">Confirm</button><button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
      $('#submit').click(function() {
        $('#submit').prop('disabled', true);
        // set the active form id
        setActive(fid, function(json) {
          traveler = json;
          initUsedForms(json, activeTable, usedTable);
        });
      });
    } else {
      $('#modalLabel').html('Use the following selected form');
      $('#modal .modal-body').html(
        '' +
          availableForms[fid].title +
          ' updated on ' +
          moment(availableForms[fid].updatedOn).format('YYYY-MM-DD HH:mm:ss')
      );
      $('#modal .modal-footer').html(
        '<button id="submit" class="btn btn-primary">Confirm</button><button data-dismiss="modal" aria-hidden="true" class="btn">Return</button>'
      );
      $('#modal').modal('show');
      $('#submit').click(function() {
        // add the new form to the traveler forms list and set it active
        $('#submit').prop('disabled', true);
        newform = {
          formId: availableForms[fid]._id,
        };
        addForm(newform, function(json) {
          traveler = json;
          initUsedForms(json, activeTable, usedTable);
        });
      });
    }
  });
});
<|endoftext|>


<commit_before>var fs = require('fs');

var t = new Date().getTime();

var file = 'www/service-worker.js';
var data = fs.readFileSync(file); //read existing contents into data
var fd = fs.openSync(file, 'w+');
var buffer = new Buffer("const CACHE_VERSION = '"+t+"';");

fs.writeSync(fd, buffer, 0, buffer.length, 0); //write new data
fs.appendFile(fd, data);
fs.close(fd);
<commit_msg>netlify test<commit_after>var fs = require('fs');

var t = new Date().getTime();

var file = 'www/service-worker.js';
var data = fs.readFileSync(file); //read existing contents into data
var fd = fs.openSync(file, 'w+');
var buffer = new Buffer("const CACHE_VERSION = '"+t+"';");

var ws = fs.writeSync(fd, buffer, 0, buffer.length, 0); //write new data
fs.appendFile(fd, data);
fs.close(fd);

console.log('ws');
console.log(ws);
<|endoftext|>


<commit_before>import React from 'react';
import * as Helper from '../../../util/helper';

export default class LeafletMapPreview extends React.Component{
	constructor(props){
		super(props)

		this.elemId = 'map-preview'
		this.marker = null
		this.map = null
	}

	componentDidMount(){
		this.makeMap(this.elemId)
	}

	componentWillReceiveProps(nextProps){

		// Remove old marker if new one selected
		if(this.marker)
			this.map.removeLayer(this.marker);

		this.map.setView(nextProps.previewCoords, nextProps.previewZoom)

		// Marker styles
	    const css = { 'background-color': "#00bcd4", "font-size": "30px" }
	    const iconHTML = 'add_location'
	    const icon = this.markerIcon("map-marker", css, {}, iconHTML)

	    this.marker = L.marker(nextProps.previewCoords, { icon: icon, opacity: 1 })
	    this.marker.addTo(this.map)
	}

	makeMap(elemId){

		let	mapID = Meteor.settings.public.MapBox.mapID,
        	accessToken = Meteor.settings.public.MapBox.accessToken;

	    Helper.errorIf(!accessToken, "Error: Mapbox access token not defined.")

	    this.map = L.map(elemId, { zoomControl: false });
    	this.map.dragging.disable();
	    this.map.touchZoom.disable();
	    this.map.doubleClickZoom.disable();
	    this.map.scrollWheelZoom.disable();
	    this.map.boxZoom.disable();
	    this.map.keyboard.disable();

	    // Map icon, tile
	    L.Icon.Default.imagePath = 'packages/bevanhunt_leaflet/images';
	    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
	    	attribution: 'Imagery from MapBox — Map data © OpenStreetMap',
	    	id: mapID,
	    	accessToken: accessToken
	    }).addTo(this.map)

	}

	markerIcon(className, css, attr, content) {
		const $icon = $('
').addClass(className).css(css).attr(attr).html(content);
		return L.divIcon({
			iconSize: [40, 54],
			iconAnchor: [20, 54],
			html: Helper.jquery2html($icon),
		});
	}

	render(){
		return (

		)
	}
}<commit_msg>Added setTimeout for setview as repeated calls freeze map preview<commit_after>import React from 'react';
import * as Helper from '../../../util/helper';

export default class LeafletMapPreview extends React.Component{
	constructor(props){
		super(props)

		this.elemId = 'map-preview'
		this.marker = null
		this.map = null
		this.previewTimeout = null
	}

	componentDidMount(){
		this.makeMap(this.elemId)
	}

	componentWillReceiveProps(nextProps){

		// Remove old marker if new one selected
		if(this.marker)
			this.map.removeLayer(this.marker)

		// Marker styles
	    const css = { 'background-color': "#00bcd4", "font-size": "30px" }
	    const iconHTML = 'add_location'
	    const icon = this.markerIcon("map-marker", css, {}, iconHTML)

	    this.marker = L.marker(nextProps.previewCoords, { icon: icon, opacity: 1 })
	    this.marker.addTo(this.map)

	    const self = this;
	    if(this.previewTimeout) Meteor.clearInterval(this.previewTimeout);
	    this.previewTimeout = Meteor.setTimeout(function(){
			self.map.setView(nextProps.previewCoords, nextProps.previewZoom,{animate: true, duration: 1})
	    }, 100)

	}

	makeMap(elemId){

		let	mapID = Meteor.settings.public.MapBox.mapID,
        	accessToken = Meteor.settings.public.MapBox.accessToken;

	    Helper.errorIf(!accessToken, "Error: Mapbox access token not defined.")

	    this.map = L.map(elemId, { zoomControl: false });
    	this.map.dragging.disable();
	    this.map.touchZoom.disable();
	    this.map.doubleClickZoom.disable();
	    this.map.scrollWheelZoom.disable();
	    this.map.boxZoom.disable();
	    this.map.keyboard.disable();

	    // Map icon, tile
	    L.Icon.Default.imagePath = 'packages/bevanhunt_leaflet/images';
	    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
	    	attribution: 'Imagery from MapBox — Map data © OpenStreetMap',
	    	id: mapID,
	    	accessToken: accessToken
	    }).addTo(this.map)

	}

	markerIcon(className, css, attr, content) {
		const $icon = $('
').addClass(className).css(css).attr(attr).html(content);
		return L.divIcon({
			iconSize: [40, 54],
			iconAnchor: [20, 54],
			html: Helper.jquery2html($icon),
		});
	}

	render(){
		return (

		)
	}
}<|endoftext|>


<commit_before><commit_msg>Adding a route guard for /dashboard.<commit_after><|endoftext|>


<commit_before>import React from 'react';
import Axios from 'axios';
import { NavLink } from 'react-router-dom';
import Config from '../../../Config';

import Editor from '../../post/editor/Editor';

var options = {
    month: "short",
    day: "numeric"
};

var statusCode = {
    HIDE: -2,
    SHOW: -1,
    SENDING: 0
}

class PostManager extends React.Component {

    constructor(props) {
        super(props);
        this.openNewPost = this.openNewPost.bind(this);

        this.state = {
            data: {
                data: [],
                error: [],
            },
            status: statusCode.HIDE
        };
    }

    componentDidMount() {
        Axios.get(Config.base_url + `news`).then(response => {
            console.log(response.data);
            this.setState({data: response.data});
        });
    }

    deletePost(id) {
        console.log("Deleting post :" + id);
        Axios.delete('/post/' + id,).then(response => {
            console.log(response.data);
            //TODO remove post from state if successful
        });
    }

    openNewPost() {
        this.setState({status: statusCode.SHOW});
    }

    getNewPostDiv() {
        if (this.state.status === statusCode.HIDE){
            return + Create new Post
        } else if (this.state.status === statusCode.SHOW){
            var post = {
                title: "",
                type: "",
                content: ""
            }
            return <Editor title="Create new Post" post={post} submit={this.submitPost} />;
        } else if (this.state.status === statusCode.SENDING){
            return
Your post is being sent...
;
        } else {
            return
Your post has been created! View it <NavLink activeClassName="active" to={{pathname: '/post/' + this.state.status}}>here.</NavLink>
;
        }
    }

    submitPost(data){
        console.log(data);
        Axios.post(Config.base_url + `post`, data, { withCredentials: true }).then(response => {
            console.log(response.data);
            if (response.data.success){
                console.log("Successfully posted post.");
            } else {
                console.log("Error: " + response.data.error);
            }
        });
    }

    getPosts() {
        if (this.state.data.success === false){
            return (

{this.state.data.error}

            );
        }

        var self = this;
        var posts = this.state.data.data.map(function(post) {
            var date = new Date(post.created_at).toLocaleDateString("en-us", options);
            return (

                        <NavLink to={{pathname: '/post/' + post.id}}>
                            {post.title}
                        </NavLink>
                        {post.author}
                        {date}
                         self.deletePost(post.id)}>Delete

                );
        });

        return (

Community Posts

                    {this.getNewPostDiv()}

{posts}

        )
    }

    render() {
        return (

{this.getPosts()}

        );
    }
}

export default PostManager;
<commit_msg>Forgot the base url<commit_after>import React from 'react';
import Axios from 'axios';
import { NavLink } from 'react-router-dom';
import Config from '../../../Config';

import Editor from '../../post/editor/Editor';

var options = {
    month: "short",
    day: "numeric"
};

var statusCode = {
    HIDE: -2,
    SHOW: -1,
    SENDING: 0
}

class PostManager extends React.Component {

    constructor(props) {
        super(props);
        this.openNewPost = this.openNewPost.bind(this);

        this.state = {
            data: {
                data: [],
                error: [],
            },
            status: statusCode.HIDE
        };
    }

    componentDidMount() {
        Axios.get(Config.base_url + `news`).then(response => {
            console.log(response.data);
            this.setState({data: response.data});
        });
    }

    deletePost(id) {
        console.log("Deleting post :" + id);
        Axios.delete(Config.base_url + '/post/' + id,).then(response => {
            console.log(response.data);
            //TODO remove post from state if successful
        });
    }

    openNewPost() {
        this.setState({status: statusCode.SHOW});
    }

    getNewPostDiv() {
        if (this.state.status === statusCode.HIDE){
            return + Create new Post
        } else if (this.state.status === statusCode.SHOW){
            var post = {
                title: "",
                type: "",
                content: ""
            }
            return <Editor title="Create new Post" post={post} submit={this.submitPost} />;
        } else if (this.state.status === statusCode.SENDING){
            return
Your post is being sent...
;
        } else {
            return
Your post has been created! View it <NavLink activeClassName="active" to={{pathname: '/post/' + this.state.status}}>here.</NavLink>
;
        }
    }

    submitPost(data){
        console.log(data);
        Axios.post(Config.base_url + `post`, data, { withCredentials: true }).then(response => {
            console.log(response.data);
            if (response.data.success){
                console.log("Successfully posted post.");
            } else {
                console.log("Error: " + response.data.error);
            }
        });
    }

    getPosts() {
        if (this.state.data.success === false){
            return (

{this.state.data.error}

            );
        }

        var self = this;
        var posts = this.state.data.data.map(function(post) {
            var date = new Date(post.created_at).toLocaleDateString("en-us", options);
            return (

                        <NavLink to={{pathname: '/post/' + post.id}}>
                            {post.title}
                        </NavLink>
                        {post.author}
                        {date}
                         self.deletePost(post.id)}>Delete

                );
        });

        return (

Community Posts

                    {this.getNewPostDiv()}

{posts}

        )
    }

    render() {
        return (

{this.getPosts()}

        );
    }
}

export default PostManager;
<|endoftext|>


<commit_before>YUI.add('module-tests', function(Y) {

    var Assert = Y.Test.Assert;

    var suite = new Y.Test.Suite('io-utils');

    Y.Test.Case.prototype.success = function (promise, fn) {
        var test = this;

        promise.then(function (value) {
            test.resume(function () {
                fn.call(this, value);
            });
        }, function (err) {
            test.resume(function () {
                throw err;
            });
        });

        return this;
    };

    Y.Test.Case.prototype.failure = function (promise, fn) {
        var test = this;

        promise.then(function (value) {
            test.resume(function () {
                throw new Error('Fulfilled instead of rejecting');
            });
        }, function (err) {
            test.resume(function () {
                fn.call(this, err);
            });
        });

        return this;
    };

    suite.add(new Y.Test.Case({
        name: 'Y.io.xhr() tests',
        'simple XHR request': function () {
            var request = Y.io.xhr('echo/get?foo=bar');

            Assert.isInstanceOf(Y.Promise, request, 'return value should be a promise');

            this.success(request, function (xhr) {
                Assert.areEqual('foo=bar', xhr.responseText, 'server response does not match an echo');
            });

            this.wait();
        },

        'request timeout': function () {
            this.failure(Y.io.xhr('delay/1', {timeout: 10}), function (err) {
                Assert.isNumber(err.status, 'Error should have a status number');
            });

            this.wait();
        },

        'request failure': function () {
            this.failure(Y.io.xhr('echo/status/404'), function (err) {
                Assert.areEqual(404, err.status, 'Error should be a 404');
            });

            this.wait();
        },

        'abort() should reject the promise': function () {
            var request = Y.io.xhr('delay/1');

            request.abort();

            this.failure(request, function (err) {
                Assert.areEqual('abort', err.statusText, 'Error should be abortion');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'HTTP verbs',

        'GET HTTP request': function () {
            this.success(Y.io.get('echo/get/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'GET request failure');
            });

            this.wait();
        },

        'POST HTTP request': function () {
            this.success(Y.io.post('echo/post/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'POST request failure');
            });

            this.wait();
        },

        'PUT HTTP request': function () {
            this.success(Y.io.put('echo/put/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'PUT request failure');
            });

            this.wait();
        },

        'DELETE HTTP request': function () {
            this.success(Y.io.DELETE('echo/delete/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'DELETE request failure');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'JSON requests',

        'simple JSON request': function () {
            this.success(Y.io.json('echo/get/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'GET request failure');
            });

            this.wait();
        },

        'catching parse error': function () {
            this.failure(Y.io.json('echo/get/?response=asdf'), function (err) {
                Assert.isInstanceOf(Error, err, 'error should be a syntax error');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'HTTP verbs with JSON',

        'GET JSON request': function () {
            this.success(Y.io.getJSON('echo/get/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'GET request failure');
            });

            this.wait();
        },

        'POST JSON request': function () {
            this.success(Y.io.postJSON('echo/post/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'POST request failure');
            });

            this.wait();
        },

        'PUT JSON request': function () {
            this.success(Y.io.putJSON('echo/put/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'PUT request failure');
            });

            this.wait();
        },

        'DELETE JSON request': function () {
            this.success(Y.io.deleteJSON('echo/delete/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'DELETE request failure');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'jsonp requests',

        'simple jsonp request': function () {
            var request = Y.io.jsonp('echo/get/?response={callback}({"foo":"bar"})');

            this.success(request, function (data) {
                Assert.areEqual('bar', data.foo, 'did not get the correct response from echoecho');
            });

            this.wait();
        },

        'jsonp request timeout': function () {
            this.failure(Y.io.jsonp('delay/1', {timeout: 50}), function (err) {
                Assert.isTrue(err.errors.length > 0, 'transaction should have an error');
            });

            this.wait();
        }
    }));

    Y.Test.Runner.add(suite);

},'', { requires: [ 'test' ] });
<commit_msg>Fix test and add test for JSON reviver<commit_after>YUI.add('module-tests', function(Y) {

    var Assert = Y.Test.Assert;

    var suite = new Y.Test.Suite('io-utils');

    Y.Test.Case.prototype.success = function (promise, fn) {
        var test = this;

        promise.then(function (value) {
            test.resume(function () {
                fn.call(this, value);
            });
        }, function (err) {
            test.resume(function () {
                Assert.fail('Promise rejected instead of fulfilled');
                throw err;
            });
        });

        return this;
    };

    Y.Test.Case.prototype.failure = function (promise, fn) {
        var test = this;

        promise.then(function (value) {
            test.resume(function () {
                Assert.fail('Promise fulfilled instead of rejected');
            });
        }, function (err) {
            test.resume(function () {
                fn.call(this, err);
            });
        });

        return this;
    };

    suite.add(new Y.Test.Case({
        name: 'Y.io.xhr() tests',
        'simple XHR request': function () {
            var request = Y.io.xhr('echo/get?foo=bar');

            Assert.isInstanceOf(Y.Promise, request, 'return value should be a promise');

            this.success(request, function (xhr) {
                Assert.areEqual('foo=bar', xhr.responseText, 'server response does not match an echo');
            });

            this.wait();
        },

        'request timeout': function () {
            this.failure(Y.io.xhr('delay/1', {timeout: 10}), function (err) {
                Assert.isNumber(err.status, 'Error should have a status number');
            });

            this.wait();
        },

        'request failure': function () {
            this.failure(Y.io.xhr('echo/status/404'), function (err) {
                Assert.areEqual(404, err.status, 'Error should be a 404');
            });

            this.wait();
        },

        'abort() should reject the promise': function () {
            var request = Y.io.xhr('delay/1');

            request.abort();

            this.failure(request, function (err) {
                Assert.areEqual('abort', err.statusText, 'Error should be abortion');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'HTTP verbs',

        'GET HTTP request': function () {
            this.success(Y.io.get('echo/get/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'GET request failure');
            });

            this.wait();
        },

        'POST HTTP request': function () {
            this.success(Y.io.post('echo/post/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'POST request failure');
            });

            this.wait();
        },

        'PUT HTTP request': function () {
            this.success(Y.io.put('echo/put/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'PUT request failure');
            });

            this.wait();
        },

        'DELETE HTTP request': function () {
            this.success(Y.io.DELETE('echo/delete/?response=helloworld'), function (xhr) {
                Assert.areEqual('helloworld', xhr.responseText, 'DELETE request failure');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'JSON requests',

        'simple JSON request': function () {
            this.success(Y.io.json('echo/get/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'GET request failure');
            });

            this.wait();
        },

        'catching parse error': function () {
            this.failure(Y.io.json('echo/get/?response=asdf'), function (err) {
                Assert.isInstanceOf(Error, err, 'error should be a syntax error');
            });

            this.wait();
        },

        'reviver function': function () {
            var date = new Date().toJSON(),
                DATEPATTERN = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/;

            var request = Y.io.json('echo/get/?response={"date":"' + date + '"}', {
                reviver: function(key, value) {
                    return DATEPATTERN.test(value) ? new Date(value) : value;
                }
            });

            this.success(request, function (data) {
                Assert.isInstanceOf(Date, data.date, 'Date was not revived');
                Assert.areSame(date, data.date.toJSON(), 'Date was not properly parsed');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'HTTP verbs with JSON',

        'GET JSON request': function () {
            this.success(Y.io.getJSON('echo/get/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'GET request failure');
            });

            this.wait();
        },

        'POST JSON request': function () {
            this.success(Y.io.postJSON('echo/json/', {"foo":"bar"}), function (data) {
                Assert.areEqual('bar', data.foo, 'POST request failure');
            });

            this.wait();
        },

        'PUT JSON request': function () {
            this.success(Y.io.putJSON('echo/json/', {"foo":"bar"}), function (data) {
                Assert.areEqual('bar', data.foo, 'PUT request failure');
            });

            this.wait();
        },

        'DELETE JSON request': function () {
            this.success(Y.io.deleteJSON('echo/delete/?response={"foo":"bar"}'), function (data) {
                Assert.areEqual('bar', data.foo, 'DELETE request failure');
            });

            this.wait();
        }
    }));

    suite.add(new Y.Test.Case({
        name: 'jsonp requests',

        'simple jsonp request': function () {
            var request = Y.io.jsonp('echo/get/?response={callback}({"foo":"bar"})');

            this.success(request, function (data) {
                Assert.areEqual('bar', data.foo, 'did not get the correct response from echoecho');
            });

            this.wait();
        },

        'jsonp request timeout': function () {
            this.failure(Y.io.jsonp('delay/1', {timeout: 50}), function (err) {
                Assert.isTrue(err.errors.length > 0, 'transaction should have an error');
            });

            this.wait();
        }
    }));

    Y.Test.Runner.add(suite);

},'', { requires: [ 'test' ] });
<|endoftext|>


<commit_before>/*!
 * Subtitle.js
 * Parse and manipulate SRT (SubRip)
 * https://github.com/gsantiago/subtitle.js
 *
 * @version 0.0.4
 * @author Guilherme Santiago
*/

/**
 * @constructor
 * @param {String} Optional SRT content to be parsed
*/
function Subtitle (srt) {
  this.subtitles = [];

  if (srt) {
    this.parse(srt);
  }
}

/**
 * SRT parser
*/
Subtitle.prototype.parse = function (srt) {
  var subs = [];
  var index;
  var time;
  var text;
  var start;
  var end;

  if (!srt) {
    throw new Error('No SRT to parse');
  }

  srt = srt.split('\n');

  srt.forEach(function (line) {
    line = line.toString();

    // if we don't have an index, so we should expect an index
    if (!index) {
      if (/^\d+$/.test(line)) {
        index = parseInt(line);
        return;
      }
    }

    // now we have to check for the time
    if (!time) {
      var match = line.match(/^(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})$/);
      if (match) {
        start = match[1];
        end = match[2];
        time = true;
        return;
      }
    }

    // now we get all the strings until we get an empty line
    if (line.trim() === '') {
      subs.push({
        index: index,
        start: start,
        end: end,
        text: text
      });
      index = time = start = end = text = null;
    } else {
      if (!text) {
        text = line;
      } else {
        text += '\n' + line;
      }
    }

  });

  this.subtitles = subs;

  return this;
};

/**
 * Add a caption
 * You have to pass an object containing the following data:
 * start - The start time
 * end - The end time
 * text - The caption text
 *
 * The start and end time support two patterns:
 * The SRT: '00:00:24,400'
 * Or a positive integer representing milliseconds
 *
 * @public
 * @param {Object} Caption data
*/
Subtitle.prototype.add = function (caption) {
  if (!caption.start || !caption.end || !caption.text) {
    throw new Error('Invalid caption data');
  }

  for (var prop in caption) {
    if (!caption.hasOwnProperty(prop) || prop === 'text') {
      continue;
    }

    if (prop === 'start' || prop === 'end') {
      if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(caption[prop])) {
        continue;
      }
      if (/^\d+$/.test(caption[prop])) {
        caption[prop] = Subtitle.toSrtTime(caption[prop]);
      } else {
        throw new Error('Invalid caption time format');
      }
    }
  }

  this.subtitles.push({
    index: this.subtitles.length + 1,
    start: caption.start,
    end: caption.end,
    text: caption.text
  });

  return this;
};

/**
 * Convert the SRT time format to milliseconds
 *
 * @static
 * @param {String} SRT time format
*/
Subtitle.toMS = function (time) {
  var match = time.match(/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/);

  if (!match) {
    throw new Error('Invalid SRT time format');
  }

  var hours = parseInt(match[1], 10);
  var minutes = parseInt(match[2], 10);
  var seconds = parseInt(match[3], 10);
  var milliseconds = parseInt(match[4], 10);

  hours *= 3600000;
  minutes *= 60000;
  seconds *= 1000;

  return hours + minutes + seconds + milliseconds;
};

/**
 * Convert milliseconds to SRT time format
 *
 * @static
 * @param {Integer} Milleseconds
*/
Subtitle.toSrtTime = function (time) {
  if (!/^\d+$/.test(time.toString())) {
    throw new Error('Time should be an Integer value in milliseconds');
  }

  time = parseInt(time);

  var date = new Date(0, 0, 0, 0, 0, 0, time);

  var hours = date.getHours() < 10
    ? '0' + date.getHours()
    : date.getHours();

  var minutes = date.getMinutes() < 10
    ? '0' + date.getMinutes()
    : date.getMinutes();

  var seconds = date.getSeconds() < 10
    ? '0' + date.getSeconds()
    : date.getSeconds();

  var ms = time - ((hours * 3600000) + (minutes * 60000) + (seconds * 1000));

  if (ms < 100 && ms > 10) {
    ms = '0' + ms;
  } else if (ms < 10) {
    ms = '00' + ms;
  }

  var srtTime = hours + ':' + minutes + ':' + seconds + ',' + ms;

  return srtTime;
};

module.exports = Subtitle;
<commit_msg>Add getSubtitles() method, and set the "subtitles" property as private<commit_after>/*!
 * Subtitle.js
 * Parse and manipulate SRT (SubRip)
 * https://github.com/gsantiago/subtitle.js
 *
 * @version 0.0.5
 * @author Guilherme Santiago
*/

/**
 * Dependencies
*/
var _ = require('underscore');

/**
 * @constructor
 * @param {String} Optional SRT content to be parsed
*/
function Subtitle (srt) {
  this._subtitles = [];

  if (srt) {
    this.parse(srt);
  }
}

/**
 * SRT parser
*/
Subtitle.prototype.parse = function (srt) {
  var subs = [];
  var index;
  var time;
  var text;
  var start;
  var end;

  if (!srt) {
    throw new Error('No SRT to parse');
  }

  srt = srt.split('\n');

  srt.forEach(function (line) {
    line = line.toString();

    // if we don't have an index, so we should expect an index
    if (!index) {
      if (/^\d+$/.test(line)) {
        index = parseInt(line);
        return;
      }
    }

    // now we have to check for the time
    if (!time) {
      var match = line.match(/^(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})$/);
      if (match) {
        start = match[1];
        end = match[2];
        time = true;
        return;
      }
    }

    // now we get all the strings until we get an empty line
    if (line.trim() === '') {
      subs.push({
        index: index,
        start: start,
        end: end,
        text: text
      });
      index = time = start = end = text = null;
    } else {
      if (!text) {
        text = line;
      } else {
        text += '\n' + line;
      }
    }

  });

  this._subtitles = subs;

  return this;
};

/**
 * Add a caption
 * You have to pass an object containing the following data:
 * start - The start time
 * end - The end time
 * text - The caption text
 *
 * The start and end time support two patterns:
 * The SRT: '00:00:24,400'
 * Or a positive integer representing milliseconds
 *
 * @public
 * @param {Object} Caption data
*/
Subtitle.prototype.add = function (caption) {
  if (!caption.start || !caption.end || !caption.text) {
    throw new Error('Invalid caption data');
  }

  for (var prop in caption) {
    if (!caption.hasOwnProperty(prop) || prop === 'text') {
      continue;
    }

    if (prop === 'start' || prop === 'end') {
      if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(caption[prop])) {
        continue;
      }
      if (/^\d+$/.test(caption[prop])) {
        caption[prop] = Subtitle.toSrtTime(caption[prop]);
      } else {
        throw new Error('Invalid caption time format');
      }
    }
  }

  this._subtitles.push({
    index: this._subtitles.length + 1,
    start: caption.start,
    end: caption.end,
    text: caption.text
  });

  return this;
};

/**
 * Convert the SRT time format to milliseconds
 *
 * @static
 * @param {String} SRT time format
*/
Subtitle.toMS = function (time) {
  var match = time.match(/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/);

  if (!match) {
    throw new Error('Invalid SRT time format');
  }

  var hours = parseInt(match[1], 10);
  var minutes = parseInt(match[2], 10);
  var seconds = parseInt(match[3], 10);
  var milliseconds = parseInt(match[4], 10);

  hours *= 3600000;
  minutes *= 60000;
  seconds *= 1000;

  return hours + minutes + seconds + milliseconds;
};

/**
 * Convert milliseconds to SRT time format
 *
 * @static
 * @param {Integer} Milleseconds
*/
Subtitle.toSrtTime = function (time) {
  if (!/^\d+$/.test(time.toString())) {
    throw new Error('Time should be an Integer value in milliseconds');
  }

  time = parseInt(time);

  var date = new Date(0, 0, 0, 0, 0, 0, time);

  var hours = date.getHours() < 10
    ? '0' + date.getHours()
    : date.getHours();

  var minutes = date.getMinutes() < 10
    ? '0' + date.getMinutes()
    : date.getMinutes();

  var seconds = date.getSeconds() < 10
    ? '0' + date.getSeconds()
    : date.getSeconds();

  var ms = time - ((hours * 3600000) + (minutes * 60000) + (seconds * 1000));

  if (ms < 100 && ms > 10) {
    ms = '0' + ms;
  } else if (ms < 10) {
    ms = '00' + ms;
  }

  var srtTime = hours + ':' + minutes + ':' + seconds + ',' + ms;

  return srtTime;
};

/**
 * Return the subtitles
 *
 * @param {Object} Options
 * @returns {Array} Subtitles
*/
Subtitle.prototype.getSubtitles = function (options) {
  var subtitles = this._subtitles;

  var defaults = {
    timeFormat: 'srt'
  };

  options = _.extendOwn(defaults, options);

  if (options.timeFormat === 'ms') {
    subtitles = _.map(subtitles, function (caption) {
      caption.start = Subtitle.toMS(caption.start);
      caption.end = Subtitle.toMS(caption.end);
      return caption;
    });
  }

  return subtitles;
};

module.exports = Subtitle;
<|endoftext|>


<commit_before>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var angular = angular || null;
var LOCK_EXPIRATION = 25;
var USER_IDLE_MINUTES = 15;

(function() {

    var module = angular.module('amara.SubtitleEditor.subtitles.directives', []);

    module.directive('subtitleEditor', function() {
        return function link(scope, elm, attrs) {
            // For some reason using ng-keydown at the HTML tag doesn't work.
            // Use jquery instead.
            $(document).keydown(function(evt) {
                scope.$apply(function(scope) {
                    scope.handleAppKeyDown(evt);
                });
            });
        };
    });
    module.directive('workingSubtitles', function() {
        return function link(scope, elem, attrs) {
            var startHelper = $('div.sync-help.begin', elem);
            var endHelper = $('div.sync-help.end', elem);
            var infoTray = $('div.info-tray', elem);
            var subtitleList = $('.subtitles ul', elem);
            var currentArrow = undefined;
            var wrapper = $(elem);

            function getSubtitleTop(index) {
                var li = $('li', subtitleList).eq(index);
                var top = li.offset().top - wrapper.offset().top;
                if(top < 0 || top + startHelper.height() >= wrapper.height()) {
                    return null;
                }
                return top;
            }

            var lastSyncStartIndex = null;
            var lastSyncEndIndex = null;

            scope.positionSyncHelpers = function(startIndex, endIndex) {
                if(!scope.timelineShown) {
                    startHelper.hide();
                    endHelper.hide();
                    return;
                }
                if(startIndex === undefined) {
                    startIndex = lastSyncStartIndex;
                }
                if(endIndex === undefined) {
                    endIndex = lastSyncEndIndex;
                }
                var startTop = null;
                var endTop = null;
                if(startIndex !== null) {
                    startTop = getSubtitleTop(startIndex);
                }
                if(endIndex !== null) {
                    endTop = getSubtitleTop(endIndex);
                }
                if(startTop !== null) {
                    startHelper.css('top', startTop + 'px');
                    startHelper.show();
                } else {
                    startHelper.hide();
                }
                if(endTop !== null) {
                    endHelper.css('top', endTop + 'px');
                    endHelper.show();
                } else {
                    endHelper.hide();
                }
                lastSyncStartIndex = startIndex;
                lastSyncEndIndex = endIndex;
            }

            scope.positionInfoTray = function() {
                var BUFFER = 22;
                var subtitle = scope.currentEdit.storedSubtitle();
                if(subtitle === null) {
                    infoTray.hide();
                    return;
                }
                var li = scope.getSubtitleRepeatItem(subtitle);
                if(li) {
                    currentArrow = li.find(".arrow");
                    var top = li.offset().top - wrapper.offset().top;
                    var bottom = top + li.height();

                    var infoTrayBottom = infoTray.height() + top;
                    var willTrim = wrapper.height() < infoTrayBottom;
                    if (willTrim) {
                        var adjustedTop = top - (infoTrayBottom - wrapper.height());
                        if (adjustedTop + BUFFER < bottom && adjustedTop - BUFFER > top - infoTray.height()) {
                            top = adjustedTop;
                        }
                    }
                    if(top + BUFFER >= 0 && top + BUFFER < wrapper.height()) {
                        infoTray.css('top', top + 'px');
                        infoTray.show();
                        currentArrow.show();
                    } else {
                        infoTray.hide();
                        currentArrow.hide();
                    }
                }
            }

            scope.$watch("currentEdit.draft", function() {
                if (currentArrow) {
                    currentArrow.hide(); // Kinda hate this, i think it would be cleaner to give each line its own controller
                }
                scope.positionInfoTray();
            });

            scope.$watch("timelineShown", function() {
                scope.positionSyncHelpers();
            });

            scope.$root.$on('working-subtitles-scrolled', function() {
                scope.positionSyncHelpers();
                scope.positionInfoTray();
            });
        };
    });

    module.directive('subtitleScroller', function($window) {
        var window = $($window);
	var scrollingPrevious = [];
	$('div.subtitles').each(function(index) {
	    scrollingPrevious[index] = $(this).scrollTop();
	});
        return function link(scope, elem, attrs) {
            var scroller = $(elem);
            var isWorkingSet = (attrs.subtitleList == "working-subtitle-set");
            // Handle scroll.
            scroller.scroll(function() {
                // If scroll sync is locked.
                if (scope.scrollingSynced) {
                    var delta = 0;
                    var index_scrolled = -1;
                    $('div.subtitles').each(function(index) {
                        var newScrollTop = $(this).scrollTop();
                        if (newScrollTop != scrollingPrevious[index]) {
                            delta = newScrollTop - scrollingPrevious[index];
                            index_scrolled = index;
                        }
                    });
                    if (index_scrolled != -1) {
                        $('div.subtitles').each(function(index) {
                            if (index != index_scrolled) {
                                var newScrollTop = $(this).scrollTop() + delta;
                                $(this).scrollTop(newScrollTop);
                                var updatedScrollTop = $(this).scrollTop();
                                if ((updatedScrollTop != newScrollTop) && (updatedScrollTop != 0)) {
                                    $(this).children().last().height($(this).children().last().height() + newScrollTop - updatedScrollTop);
                                    $(this).scrollTop(newScrollTop);
                                }
                            }
                        });
                    }
                }
		$('div.subtitles').each(function(index) {
		    scrollingPrevious[index] = $(this).scrollTop();
		});

                if(isWorkingSet) {
                    scope.$root.$emit("working-subtitles-scrolled");
                }
            });

            function resizeScroller() {
                if (scope.timelineShown) {
                    var scrollerTop = 398;
                } else {
                    var scrollerTop = 327;
                }
                scroller.height(window.height() - scrollerTop);
            }

            scope.$watch('timelineShown', resizeScroller);
            window.on('resize', resizeScroller);
            resizeScroller();

            if (isWorkingSet) {
                scope.$root.$on('scroll-to-subtitle', function(evt, subtitle) {
                    if(scope.currentEdit.inProgress()) {
                        return;
                    }
                    var target = scope.getSubtitleRepeatItem(subtitle);
                    var prev = target.prev();
                    if(prev.length > 0) {
                        target = prev;
                    }
                    if(target) {
                        scroller.scrollTop(scroller.scrollTop() +
                                target.offset().top - scroller.offset().top);
                    }
                });
            }
        }
    });

    module.directive('subtitleRepeat', function($interpolate, $filter, $parse,
                DomUtil, EditorData) {
        /* Specialized repeat directive to work with subtitleList
         *
         * Because we need to deal potentially thousands of subtitles,
         * ng-repeat is not ideal.  subtitleRepeat is a specialized directive
         * that creates the
 that we need for a subtitle list.  The
         * speedups come from a couple things:
         *
         *   - It hooks up to the change callback of subtitleList to calculate
         *   changes quickly.
         *   - it doesn't create a child scope for each subtitle.
         *
         *  When using subtitle-repeat, set the value of subtitle-repeat to
         *  the subtitleList to bind to.  Optionally, create a read-only
         *  attribute to specify a read-only list.
         */
        return function link($scope, elm, attrs) {
            var subtitleList = $scope.$eval(attrs.subtitleRepeat);
            // Map subtitle ID to DOM node for that subtitle
            var subtitleMap = {};
            // are we a read-only list?
            var readOnly = Boolean(attrs.readOnly);
            var displayTime = $filter('displayTime');
            // Template elements that we use to create list items
            var templateLI = $('
');
            templateLI.append('');
            templateLI.append('');
            if(!readOnly) {
                templateLI.append(makeImageButton('remove-subtitle',
                    'images/editor/remove-subtitle.gif'));
                templateLI.append(makeImageButton('insert-subtitle',
                    'images/editor/plus.gif'));
                templateLI.append(
                        '<button class="new-paragraph">¶</button>');
            } else {
                templateLI.append('¶');
            }

            $scope.getSubtitleRepeatItem = function(subtitle) {
                var rv = subtitleMap[subtitle.id];
                if(rv !== undefined) {
                    return rv;
                } else {
                    return null;
                }
            }

            if(!readOnly) {
                $scope.$watch('currentEdit.draft', function(newValue, oldValue) {
                    if(oldValue) {
                        stopEditOn(oldValue);
                    }
                    if(newValue) {
                        startEditOn(newValue);
                    }
                });

                elm.on('click', function(evt) {
                    var action = findSubtitleClick(evt.target);
                    var subtitle = findSubtitleData(evt.target);
                    if(action && $scope.onSubtitleClick) {
                        $scope.$apply(function() {
                            $scope.onSubtitleClick(evt, subtitle, action);
                        });
                    }
                });
            }

            $scope.$watch('timeline.shownSubtitle', function(newSub, oldSub) {
                if(newSub && subtitleMap[newSub.id]) {
                    subtitleMap[newSub.id].addClass('current-subtitle');
                }
                if(oldSub && subtitleMap[oldSub.id]) {
                    subtitleMap[oldSub.id].removeClass('current-subtitle');
                }
            });

            subtitleList.addChangeCallback(onChange);
            reloadSubtitles();

            function makeImageButton(cssClass, imageURLPath) {
                var img = $('');
                img.prop('src', EditorData.staticURL + imageURLPath);
                var button = $('<button />');
                button.prop('class', cssClass);
                button.append(img);
                return button
            }

            function createLIForSubtitle(subtitle) {
                var elt = templateLI.clone();
                renderSubtitle(subtitle, elt);
                subtitleMap[subtitle.id] = elt;
                elt.data('subtitle', subtitle);
                return elt;
            }

            function renderSubtitle(subtitle, elt) {
                var content = subtitle.content();
                var classes = [];

                if($scope.timeline.shownSubtitle === subtitle) {
                    classes.push('current-subtitle');
                }
                if(content == '') {
                    classes.push('empty');
                }
                if(subtitle.startOfParagraph) {
                    classes.push('paragraph-start');
                }
                elt.prop('className', classes.join(' '));
                $('span.subtitle-text', elt).html(content);
                $('span.timing', elt).text(displayTime(subtitle.startTime));
            }

            function findSubtitleData(node) {
                // Find the subtitle that we set with the jquery data()
                // function by starting with node and moving up the DOM tree.
                var toplevelNode = elm[0];
                while(node && node != toplevelNode) {
                    var subtitle = $(node).data('subtitle');
                    if(subtitle) {
                        return subtitle;
                    } else {
                        node = node.parentNode;
                    }
                }
                return null;
            }

            function findSubtitleClick(node, dataName) {
                var toplevelNode = elm[0];
                while(node && node != toplevelNode) {
                    if(node.tagName == 'BUTTON') {
                        switch(node.className) {
                            case 'insert-subtitle':
                                return 'insert';

                            case 'remove-subtitle':
                                return 'remove';

                            case 'new-paragraph':
                                return 'changeParagraph'
                        }
                    } else if(node.tagName == 'LI') {
                        return 'edit';
                    }
                    node = node.parentNode;
                }
                return null;
            }

            function onChange(change) {
                var subtitle = change.subtitle;
                switch(change.type) {
                    case 'remove':
                        var node = subtitleMap[subtitle.id];
                        node.remove();
                        delete subtitleMap[subtitle.id];
                        break;
                    case 'update':
                        renderSubtitle(subtitle, subtitleMap[subtitle.id]);
                        break;
                    case 'insert':
                        if(change.before !== null) {
                            var node = subtitleMap[change.before.id];
                            node.before(createLIForSubtitle(subtitle));
                        } else {
                            elm.append(createLIForSubtitle(subtitle));
                        }
                        break;
                    case 'reload':
                        reloadSubtitles();
                }
            }

            function startEditOn(draft) {
                var li = subtitleMap[draft.storedSubtitle.id];
                li.addClass('edit');
                var textarea = $('<textarea class="subtitle-edit" />');
                textarea.val(draft.markdown);
                li.append(textarea);
                textarea.autosize();
                textarea.focus();
                if(draft.initialCaretPos === undefined) {
                    var caretPos = draft.markdown.length;
                } else {
                    var caretPos = draft.initialCaretPos;
                }
                DomUtil.setSelectionRange(textarea[0], caretPos, caretPos);
                textarea.on('keyup', function(evt) {
                    $scope.$apply(function() {
                        draft.markdown = textarea.val();
                    });
                });
                if($scope.onEditKeydown) {
                    textarea.on('keydown', function(evt) {
                        $scope.$apply(function() {
                            $scope.onEditKeydown(evt);
                        });
                    });
                }
            }
            function stopEditOn(draft) {
                var li = subtitleMap[draft.storedSubtitle.id];
                if(li) {
                    li.removeClass('edit');
                    $('textarea.subtitle-edit', li).remove();
                }
            }

            function reloadSubtitles() {
                elm.empty();
                subtitleMap = {}
                _.each(subtitleList.subtitles, function(subtitle) {
                    elm.append(createLIForSubtitle(subtitle));
                });
            }
        }
    });

    module.directive('languageSelector', function(SubtitleStorage) {
        return {
            compile: function compile(elm, attrs, transclude) {
                return {
                    post: function post(scope, elm, attrs) {
                        /* For some reason, if we use ng-select angular
                         * creates an extra option because it thinks
                         * scope.versionNumber is an invalid value for the
                         * options, even though it isn't.  I think it has to
                         * do with the fact that we manually create options
                         * with ng-repeat.  In any case, we handle the
                         * select ourself.
                         */
                        $('select.version-select', elm).change(function(evt) {
                            scope.versionNumber = this.value;
                            scope.$digest();
                        });
                        scope.setInitialDisplayLanguage(SubtitleStorage.getLanguages());
                    }
                };
            }
        };
    });
})();
<commit_msg>Fixed scrolling the sync helpers (#1195)<commit_after>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var angular = angular || null;
var LOCK_EXPIRATION = 25;
var USER_IDLE_MINUTES = 15;

(function() {

    var module = angular.module('amara.SubtitleEditor.subtitles.directives', []);

    module.directive('subtitleEditor', function() {
        return function link(scope, elm, attrs) {
            // For some reason using ng-keydown at the HTML tag doesn't work.
            // Use jquery instead.
            $(document).keydown(function(evt) {
                scope.$apply(function(scope) {
                    scope.handleAppKeyDown(evt);
                });
            });
        };
    });
    module.directive('workingSubtitles', function() {
        return function link(scope, elem, attrs) {
            var startHelper = $('div.sync-help.begin', elem);
            var endHelper = $('div.sync-help.end', elem);
            var infoTray = $('div.info-tray', elem);
            var subtitleList = $('.subtitles ul', elem);
            var currentArrow = undefined;
            var wrapper = $(elem);

            function getSubtitleTop(index) {
                var li = $('li', subtitleList).eq(index);
                var top = li.offset().top - wrapper.offset().top;
                if(top < 0 || top + startHelper.height() >= wrapper.height()) {
                    return null;
                }
                return top;
            }

            var lastSyncStartIndex = null;
            var lastSyncEndIndex = null;

            scope.positionSyncHelpers = function(startIndex, endIndex) {
                if(!scope.timelineShown) {
                    startHelper.hide();
                    endHelper.hide();
                    return;
                }
                if(startIndex === undefined) {
                    startIndex = lastSyncStartIndex;
                }
                if(endIndex === undefined) {
                    endIndex = lastSyncEndIndex;
                }
                var startTop = null;
                var endTop = null;
                if(startIndex !== null) {
                    startTop = getSubtitleTop(startIndex);
                }
                if(endIndex !== null) {
                    endTop = getSubtitleTop(endIndex);
                }
                if(startTop !== null) {
                    startHelper.css('top', startTop + 'px');
                    startHelper.show();
                } else {
                    startHelper.hide();
                }
                if(endTop !== null) {
                    endHelper.css('top', endTop + 'px');
                    endHelper.show();
                } else {
                    endHelper.hide();
                }
                lastSyncStartIndex = startIndex;
                lastSyncEndIndex = endIndex;
            }

            scope.positionInfoTray = function() {
                var BUFFER = 22;
                var subtitle = scope.currentEdit.storedSubtitle();
                if(subtitle === null) {
                    infoTray.hide();
                    return;
                }
                var li = scope.getSubtitleRepeatItem(subtitle);
                if(li) {
                    currentArrow = li.find(".arrow");
                    var top = li.offset().top - wrapper.offset().top;
                    var bottom = top + li.height();

                    var infoTrayBottom = infoTray.height() + top;
                    var willTrim = wrapper.height() < infoTrayBottom;
                    if (willTrim) {
                        var adjustedTop = top - (infoTrayBottom - wrapper.height());
                        if (adjustedTop + BUFFER < bottom && adjustedTop - BUFFER > top - infoTray.height()) {
                            top = adjustedTop;
                        }
                    }
                    if(top + BUFFER >= 0 && top + BUFFER < wrapper.height()) {
                        infoTray.css('top', top + 'px');
                        infoTray.show();
                        currentArrow.show();
                    } else {
                        infoTray.hide();
                        currentArrow.hide();
                    }
                }
            }

            scope.$watch("currentEdit.draft", function() {
                if (currentArrow) {
                    currentArrow.hide(); // Kinda hate this, i think it would be cleaner to give each line its own controller
                }
                scope.positionInfoTray();
            });

            scope.$watch("timelineShown", function() {
                scope.positionSyncHelpers();
            });

            scope.$root.$on('working-subtitles-scrolled', function() {
                scope.positionSyncHelpers();
                scope.positionInfoTray();
            });
        };
    });

    module.directive('subtitleScroller', function($window) {
        var window = $($window);
	var scrollingPrevious = [];
	$('div.subtitles').each(function(index) {
	    scrollingPrevious[index] = $(this).scrollTop();
	});
        return function link(scope, elem, attrs) {
            var scroller = $(elem);
            var isWorkingSet = (attrs.subtitleScroller == "working-subtitle-set");
            // Handle scroll.
            scroller.scroll(function() {
                // If scroll sync is locked.
                if (scope.scrollingSynced) {
                    var delta = 0;
                    var index_scrolled = -1;
                    $('div.subtitles').each(function(index) {
                        var newScrollTop = $(this).scrollTop();
                        if (newScrollTop != scrollingPrevious[index]) {
                            delta = newScrollTop - scrollingPrevious[index];
                            index_scrolled = index;
                        }
                    });
                    if (index_scrolled != -1) {
                        $('div.subtitles').each(function(index) {
                            if (index != index_scrolled) {
                                var newScrollTop = $(this).scrollTop() + delta;
                                $(this).scrollTop(newScrollTop);
                                var updatedScrollTop = $(this).scrollTop();
                                if ((updatedScrollTop != newScrollTop) && (updatedScrollTop != 0)) {
                                    $(this).children().last().height($(this).children().last().height() + newScrollTop - updatedScrollTop);
                                    $(this).scrollTop(newScrollTop);
                                }
                            }
                        });
                    }
                }
		$('div.subtitles').each(function(index) {
		    scrollingPrevious[index] = $(this).scrollTop();
		});

                if(isWorkingSet) {
                    scope.$root.$emit("working-subtitles-scrolled");
                }
            });

            function resizeScroller() {
                if (scope.timelineShown) {
                    var scrollerTop = 398;
                } else {
                    var scrollerTop = 327;
                }
                scroller.height(window.height() - scrollerTop);
            }

            scope.$watch('timelineShown', resizeScroller);
            window.on('resize', resizeScroller);
            resizeScroller();

            if (isWorkingSet) {
                scope.$root.$on('scroll-to-subtitle', function(evt, subtitle) {
                    if(scope.currentEdit.inProgress()) {
                        return;
                    }
                    var target = scope.getSubtitleRepeatItem(subtitle);
                    var prev = target.prev();
                    if(prev.length > 0) {
                        target = prev;
                    }
                    if(target) {
                        scroller.scrollTop(scroller.scrollTop() +
                                target.offset().top - scroller.offset().top);
                    }
                });
            }
        }
    });

    module.directive('subtitleRepeat', function($interpolate, $filter, $parse,
                DomUtil, EditorData) {
        /* Specialized repeat directive to work with subtitleList
         *
         * Because we need to deal potentially thousands of subtitles,
         * ng-repeat is not ideal.  subtitleRepeat is a specialized directive
         * that creates the
 that we need for a subtitle list.  The
         * speedups come from a couple things:
         *
         *   - It hooks up to the change callback of subtitleList to calculate
         *   changes quickly.
         *   - it doesn't create a child scope for each subtitle.
         *
         *  When using subtitle-repeat, set the value of subtitle-repeat to
         *  the subtitleList to bind to.  Optionally, create a read-only
         *  attribute to specify a read-only list.
         */
        return function link($scope, elm, attrs) {
            var subtitleList = $scope.$eval(attrs.subtitleRepeat);
            // Map subtitle ID to DOM node for that subtitle
            var subtitleMap = {};
            // are we a read-only list?
            var readOnly = Boolean(attrs.readOnly);
            var displayTime = $filter('displayTime');
            // Template elements that we use to create list items
            var templateLI = $('
');
            templateLI.append('');
            templateLI.append('');
            if(!readOnly) {
                templateLI.append(makeImageButton('remove-subtitle',
                    'images/editor/remove-subtitle.gif'));
                templateLI.append(makeImageButton('insert-subtitle',
                    'images/editor/plus.gif'));
                templateLI.append(
                        '<button class="new-paragraph">¶</button>');
            } else {
                templateLI.append('¶');
            }

            $scope.getSubtitleRepeatItem = function(subtitle) {
                var rv = subtitleMap[subtitle.id];
                if(rv !== undefined) {
                    return rv;
                } else {
                    return null;
                }
            }

            if(!readOnly) {
                $scope.$watch('currentEdit.draft', function(newValue, oldValue) {
                    if(oldValue) {
                        stopEditOn(oldValue);
                    }
                    if(newValue) {
                        startEditOn(newValue);
                    }
                });

                elm.on('click', function(evt) {
                    var action = findSubtitleClick(evt.target);
                    var subtitle = findSubtitleData(evt.target);
                    if(action && $scope.onSubtitleClick) {
                        $scope.$apply(function() {
                            $scope.onSubtitleClick(evt, subtitle, action);
                        });
                    }
                });
            }

            $scope.$watch('timeline.shownSubtitle', function(newSub, oldSub) {
                if(newSub && subtitleMap[newSub.id]) {
                    subtitleMap[newSub.id].addClass('current-subtitle');
                }
                if(oldSub && subtitleMap[oldSub.id]) {
                    subtitleMap[oldSub.id].removeClass('current-subtitle');
                }
            });

            subtitleList.addChangeCallback(onChange);
            reloadSubtitles();

            function makeImageButton(cssClass, imageURLPath) {
                var img = $('');
                img.prop('src', EditorData.staticURL + imageURLPath);
                var button = $('<button />');
                button.prop('class', cssClass);
                button.append(img);
                return button
            }

            function createLIForSubtitle(subtitle) {
                var elt = templateLI.clone();
                renderSubtitle(subtitle, elt);
                subtitleMap[subtitle.id] = elt;
                elt.data('subtitle', subtitle);
                return elt;
            }

            function renderSubtitle(subtitle, elt) {
                var content = subtitle.content();
                var classes = [];

                if($scope.timeline.shownSubtitle === subtitle) {
                    classes.push('current-subtitle');
                }
                if(content == '') {
                    classes.push('empty');
                }
                if(subtitle.startOfParagraph) {
                    classes.push('paragraph-start');
                }
                elt.prop('className', classes.join(' '));
                $('span.subtitle-text', elt).html(content);
                $('span.timing', elt).text(displayTime(subtitle.startTime));
            }

            function findSubtitleData(node) {
                // Find the subtitle that we set with the jquery data()
                // function by starting with node and moving up the DOM tree.
                var toplevelNode = elm[0];
                while(node && node != toplevelNode) {
                    var subtitle = $(node).data('subtitle');
                    if(subtitle) {
                        return subtitle;
                    } else {
                        node = node.parentNode;
                    }
                }
                return null;
            }

            function findSubtitleClick(node, dataName) {
                var toplevelNode = elm[0];
                while(node && node != toplevelNode) {
                    if(node.tagName == 'BUTTON') {
                        switch(node.className) {
                            case 'insert-subtitle':
                                return 'insert';

                            case 'remove-subtitle':
                                return 'remove';

                            case 'new-paragraph':
                                return 'changeParagraph'
                        }
                    } else if(node.tagName == 'LI') {
                        return 'edit';
                    }
                    node = node.parentNode;
                }
                return null;
            }

            function onChange(change) {
                var subtitle = change.subtitle;
                switch(change.type) {
                    case 'remove':
                        var node = subtitleMap[subtitle.id];
                        node.remove();
                        delete subtitleMap[subtitle.id];
                        break;
                    case 'update':
                        renderSubtitle(subtitle, subtitleMap[subtitle.id]);
                        break;
                    case 'insert':
                        if(change.before !== null) {
                            var node = subtitleMap[change.before.id];
                            node.before(createLIForSubtitle(subtitle));
                        } else {
                            elm.append(createLIForSubtitle(subtitle));
                        }
                        break;
                    case 'reload':
                        reloadSubtitles();
                }
            }

            function startEditOn(draft) {
                var li = subtitleMap[draft.storedSubtitle.id];
                li.addClass('edit');
                var textarea = $('<textarea class="subtitle-edit" />');
                textarea.val(draft.markdown);
                li.append(textarea);
                textarea.autosize();
                textarea.focus();
                if(draft.initialCaretPos === undefined) {
                    var caretPos = draft.markdown.length;
                } else {
                    var caretPos = draft.initialCaretPos;
                }
                DomUtil.setSelectionRange(textarea[0], caretPos, caretPos);
                textarea.on('keyup', function(evt) {
                    $scope.$apply(function() {
                        draft.markdown = textarea.val();
                    });
                });
                if($scope.onEditKeydown) {
                    textarea.on('keydown', function(evt) {
                        $scope.$apply(function() {
                            $scope.onEditKeydown(evt);
                        });
                    });
                }
            }
            function stopEditOn(draft) {
                var li = subtitleMap[draft.storedSubtitle.id];
                if(li) {
                    li.removeClass('edit');
                    $('textarea.subtitle-edit', li).remove();
                }
            }

            function reloadSubtitles() {
                elm.empty();
                subtitleMap = {}
                _.each(subtitleList.subtitles, function(subtitle) {
                    elm.append(createLIForSubtitle(subtitle));
                });
            }
        }
    });

    module.directive('languageSelector', function(SubtitleStorage) {
        return {
            compile: function compile(elm, attrs, transclude) {
                return {
                    post: function post(scope, elm, attrs) {
                        /* For some reason, if we use ng-select angular
                         * creates an extra option because it thinks
                         * scope.versionNumber is an invalid value for the
                         * options, even though it isn't.  I think it has to
                         * do with the fact that we manually create options
                         * with ng-repeat.  In any case, we handle the
                         * select ourself.
                         */
                        $('select.version-select', elm).change(function(evt) {
                            scope.versionNumber = this.value;
                            scope.$digest();
                        });
                        scope.setInitialDisplayLanguage(SubtitleStorage.getLanguages());
                    }
                };
            }
        };
    });
})();
<|endoftext|>


<commit_before>var RepositoryView = Backbone.View.extend({
  initialize: function(args) {
    _.bindAll(this, 'bind', 'unbind', 'render', 'repository_changed', 'build_log');

    this.app = args.app;
    this.repository = args.repository;
    this.repository_template = args.templates['repositories/show'];
    this.build_template = args.templates['builds/_summary'];
    this.element = $('#right');
  },
  bind: function() {
    Backbone.Events.bind.apply(this, arguments);
    this.repository.bind('change', this.repository_changed);
    this.app.bind('build:log', this.build_log);
  },
  unbind: function() {
    Backbone.Events.unbind.apply(this, arguments);
    this.app.unbind('build:updated', this.build_log);
    if(this.repository) this.repository.unbind('change', this.repository_changed);
  },
  render: function(repository) {
    this.repository = repository;
    this.bind();
    this.element.html($(this.repository_template(this.repository.toJSON())));
    $('.log', this.element).deansi();
  },
  repository_changed: function(repository) {
    // happens on build:started and build:finished
    this.update_summary(repository.id, repository.build.toJSON());
    this.update_log(repository.id, repository.build.get('log'));
  },
  build_log: function(data) {
    this.append_log(data.repository.id, data.append_log);
  },
  update_summary: function(id, attributes) {
    $('#repository_' + id + ' .summary', this.element).replaceWith($(this.build_template(attributes)));
  },
  update_log: function(id, log) {
    var element = $('#repository_' + id + ' .log', this.element);
    element.html(log);
    element.deansi();
  },
  append_log: function(id, chars) {
    var element = $('#repository_' + id + ' .log', this.element);
    element.append(chars);
    element.deansi();
  },
});

<commit_msg>fix repository_view binding<commit_after>var RepositoryView = Backbone.View.extend({
  initialize: function(args) {
    _.bindAll(this, 'bind', 'unbind', 'render', 'repository_changed', 'build_log');

    this.app = args.app;
    this.repository = args.repository;
    this.repository_template = args.templates['repositories/show'];
    this.build_template = args.templates['builds/_summary'];
    this.element = $('#right');
  },
  bind: function() {
    Backbone.Events.bind.apply(this, arguments);
    this.app.bind('build:log', this.build_log);
    this.repository.bind('change', this.repository_changed);
  },
  unbind: function() {
    Backbone.Events.unbind.apply(this, arguments);
    this.app.unbind('build:log', this.build_log);
    if(this.repository) this.repository.unbind('change', this.repository_changed);
  },
  render: function(repository) {
    this.repository = repository;
    this.bind();
    this.element.html($(this.repository_template(this.repository.toJSON())));
    $('.log', this.element).deansi();
  },
  repository_changed: function(repository) {
    // happens on build:started and build:finished
    this.update_summary(repository.id, repository.build.toJSON());
    this.update_log(repository.id, repository.build.get('log'));
  },
  build_log: function(data) {
    this.append_log(data.repository.id, data.append_log);
  },
  update_summary: function(id, attributes) {
    $('#repository_' + id + ' .summary', this.element).replaceWith($(this.build_template(attributes)));
  },
  update_log: function(id, log) {
    var element = $('#repository_' + id + ' .log', this.element);
    element.html(log);
    element.deansi();
  },
  append_log: function(id, chars) {
    var element = $('#repository_' + id + ' .log', this.element);
    element.append(chars);
    element.deansi();
  },
});

<|endoftext|>