<commit_before>//Droplit v 0.0.1
//The most ghetto of drag and drop plugins.

function dragAndDropify(queryElement) {
  //The global fileList. Contains a mapping between input[type="files"] and the files.
  //Maybe convert this to a prototypical object?
  var fileListObject = {};

  //Contains the mapping between input[type="files"] and <ul> element to list the files
  //Maybe convert this to a prototypical object?
  var fileUlObject = {};

  var form =  document.querySelector(queryElement);
  if (!form || form.nodeName != "FORM") {
    throw new Error('This is not a form! Make sure you entered the right selector!');
  }

  const inputFiles = form.querySelectorAll('input[type="file"]');
  if(inputFiles.length === 0) {
    throw new Error('Woah there cowboy! Can\'t make a drag and drop file upload if there are no file inputs!');
  }

  const preventDefault = (e) => {
    e.preventDefault();
  };

  const submitForm = (e) => {
    e.stopPropagation();
    e.preventDefault();
    const formData = new FormData(form);

    //move the files into the form data.
    for (let fileList in fileListObject) {
      fileListObject[fileList].forEach((file) => {
        formData.append(fileList, file);
      });
    }

    var request = new XMLHttpRequest();
    request.open(form.method, form.action);
    request.onload = function (msg) {
      console.log("SERVER IS DONE DOING OUR SHIT");
      window.location.href = this.responseURL;
    };
    request.send(formData);
  };

  form.addEventListener('submit', submitForm, false);

  //Prevent the window from intercepting our drag events.
  window.addEventListener('dragover', preventDefault, false);
  window.addEventListener('drop', preventDefault, false);

  //Start mutating the DOM so we can have our cake
  for (let i = 0; i < inputFiles.length; i++) {
    let input = inputFiles[i];
    let parentNode = input.parentNode;

    let d = document.createElement('div');
    d.className = 'remets-dnd';
    d.innerHTML = 'DRAG YO STUFF HERE'; //might want to remove this?
    parentNode.appendChild(d);

    input.remove();
    d.appendChild(input);

    let ul = document.createElement('ul');
    ul.className = 'remets-dnd__file-list';
    parentNode.appendChild(ul);

    fileUlObject[input.name] = ul;
    fileListObject[input.name] = [];
    dropMagic(d, input);
  }

  //Naming things is hard
  function dropMagic(wrapper, input) {

    function pushFiles(files) {
      for(let i = 0; i < files.length ; i++){
        let li = document.createElement('li');
        let content = document.createTextNode(files[i].name);
        li.appendChild(content);
        fileListObject[input.name].push(files[i]);
        fileUlObject[input.name].appendChild(li);

        let a  = document.createElement('a');
        a.href = '#';
        a.innerText = 'X';
        a.addEventListener('click', (e) => {
          let index = fileListObject[input.name].length;
          fileListObject[input.name].splice(fileListObject[input.name][index], 1);
          li.remove();
        });
        li.appendChild(a);
      }
    };

    function dragEnterAndOver(e) {
      e.stopPropagation();
      e.preventDefault();
      this.classList.add('remets-dnd-wrapper__enter');
    };

    function dragEnd(e) {
      this.classList.remove('remets-dnd-wrapper__enter');
    }

    function drop(e) {
      e.stopPropagation();
      e.preventDefault();
      this.classList.remove('remets-dnd-wrapper__enter');
      let dt = e.dataTransfer;
      let files = dt.files;
      pushFiles(files);
      console.log(`Currently, we have ${fileListObject[input.name].length} files`);
    };

    //Bind a ton of listeners...
    wrapper.addEventListener('dragenter', dragEnterAndOver, false);
    wrapper.addEventListener('dragover', dragEnterAndOver, false);
    wrapper.addEventListener('drop', drop, false);
    wrapper.addEventListener('dragleave', dragEnd)
    wrapper.addEventListener('dragexit', dragEnd);
    wrapper.addEventListener('dragend', dragEnd);

  };

}
<commit_msg>fun refactoring<commit_after>//Droplit v 0.0.1
//The most ghetto of drag and drop plugins.

function dragAndDropify(queryElement) {
  //The global fileList. Contains a mapping between input[type="files"] and the files.
  //Maybe convert this to a prototypical object?
  var fileListObject = (function () {
    var o = {}

    function addNameKey(namekey) {
      o[namekey] = [];
    };

    function pushFilesInto(files, nameKey){
      for(let i = 0; i < files.length ; i++) {
        o[nameKey].push(files[i]);
      }
    };

    function removeFileFrom(index, nameKey) {
      o[nameKey].splice(o[nameKey][index], 1);
    };

    function lenghtFrom(nameKey) {
      return o[nameKey].length;
    };

    function getFileListObject() {
      return o;
    }

    return {
      addNameKey: addNameKey,
      pushFilesInto: pushFilesInto,
      removeFileFrom: removeFileFrom,
      lenghtFrom: lenghtFrom,
      getFileListObject: getFileListObject
    };
  })();

  var fileUlObject = (function(fileListObject) {
    var o = {}

    function addNameKey(namekey) {
      o[namekey] = null;
    };

    function createUlElementInto(nameKey) {
      let ul = document.createElement('ul');
      ul.className = 'remets-dnd__file-list';
      o[nameKey] = ul;
      return ul;
    };

    function appendLiElementInto(li, nameKey) {
      o[nameKey].appendChild(li);
    };

    function createListItem(text) {
      const li = document.createElement('li');
      const content = document.createTextNode(text);
      li.appendChild(content);
      return li;
    };

    function createRemoveElement(nameKey, li, index) {
       const a  = document.createElement('a');
       a.dataset.index = index
       a.innerText = 'X';
       a.addEventListener('click', function(e) {
          const index = Number.parseInt(this.dataset.index);
          fileListObject.removeFileFrom(index, nameKey);
          li.remove();
        });
       return a;
    }

    function createLiFromFiles(files, nameKey) {
      var index = fileListObject.lenghtFrom(nameKey);
      for(let i = 0; i < files.length ; i++){
        const li = createListItem(files[i].name);
        appendLiElementInto(li, nameKey);
        const a = createRemoveElement(nameKey, li, (index + i));
        li.appendChild(a);
      }
    }

    return {
      addNameKey: addNameKey,
      createUlElementInto: createUlElementInto,
      createLiFromFiles: createLiFromFiles,
    };
  })(fileListObject);

  //Contains the mapping between input[type="files"] and <ul> element to list the files
  //Maybe convert this to a prototypical object?

  var form =  document.querySelector(queryElement);
  if (!form || form.nodeName != "FORM") {
    throw new Error('This is not a form! Make sure you entered the right selector!');
  }

  const inputFiles = form.querySelectorAll('input[type="file"]');
  if(inputFiles.length === 0) {
    throw new Error('Woah there cowboy! Can\'t make a drag and drop file upload if there are no file inputs!');
  }

  //Prevent the window from intercepting our drag events.
  window.addEventListener('dragover', preventDefault, false);
  window.addEventListener('drop', preventDefault, false);

  const preventDefault = (e) => {
    e.preventDefault();
  };

  const submitForm = (e) => {
    e.stopPropagation();
    e.preventDefault();

    const formData = new FormData(form);

    //move the files into the form data.
    for (let fileList in fileListObject.getFileListObject()) {
      fileListObject.getFileListObject()[fileList].forEach((file) => {
        formData.append(fileList, file);
      });
    }

    var request = new XMLHttpRequest();
    request.open(form.method, form.action);
    request.onload = function() {
      window.location.href = this.responseURL;
      debugger
    };
    request.send(formData);
  };

  form.addEventListener('submit', submitForm, false);

  //Start mutating the DOM so we can have our cake
  for (let i = 0; i < inputFiles.length; i++) {
    let input = inputFiles[i];
    let parentNode = input.parentNode;

    let d = document.createElement('div');
    d.className = 'remets-dnd';
    d.innerHTML = 'DRAG YO STUFF HERE'; //might want to remove this?
    parentNode.appendChild(d);

    input.remove();
    d.appendChild(input);

    let ul = fileUlObject.createUlElementInto(input.name);
    parentNode.appendChild(ul);
    fileListObject.addNameKey(input.name);
    dropMagic(d, input);
  }

  //Naming things is hard
  function dropMagic(wrapper, input) {

    function pushFiles(files) {
      fileListObject.pushFilesInto(files, input.name);
      fileUlObject.createLiFromFiles(files, input.name);
    };

    function dragEnterAndOver(e) {
      e.stopPropagation();
      e.preventDefault();
      this.classList.add('remets-dnd-wrapper__enter');
    };

    function dragEnd(e) {
      this.classList.remove('remets-dnd-wrapper__enter');
    }

    function drop(e) {
      e.stopPropagation();
      e.preventDefault();
      this.classList.remove('remets-dnd-wrapper__enter');
      let dt = e.dataTransfer;
      let files = dt.files;
      pushFiles(files);
      console.log(`Currently, we have ${fileListObject.lenghtFrom(input.name)} files`);
    };

    //Bind a ton of listeners...
    wrapper.addEventListener('dragenter', dragEnterAndOver, false);
    wrapper.addEventListener('dragover', dragEnterAndOver, false);
    wrapper.addEventListener('drop', drop, false);
    wrapper.addEventListener('dragleave', dragEnd)
    wrapper.addEventListener('dragexit', dragEnd);
    wrapper.addEventListener('dragend', dragEnd);

  };

}
<|endoftext|>


<commit_before>var jobsworth = jobsworth || {}
jobsworth.tasks = jobsworth.tasks || {}

jobsworth.tasks.TaskTimer = (function(){

  function bind() {
      var $ = jQuery;
      var self = this;

      // buttons
      var $inline = $('#timer-bar-elapsed'),
          $pause_button = $('#pause-btn'),
          $play_button  = $('#play-btn'),
          $pin_button   = $('#pin-btn'),
          $save_button  = $('#save-btn'),
          $li_elapsed = $('#worklog-elapsed'),
          $li_custom = $('#worklog-custom'),
          $li_none = $('#worklog-none'),
          $dialog = $('#worktime-dialog'),
          $form = $('#taskform');

      // initialize modal
      $dialog.draggable({handle: '.modal-header'});

      // bind dropdown
      $('.dropdown-toggle').dropdown();

      // bindings
      $pause_button.bind('click', function() {
          self.total_milliseconds += (new Date() - self.last_start_point);

          clearInterval(self.timer);
          $(this).hide();
          $play_button.show();

          return false;
      });

      $('#worklog-property ul li').bind('click', function() {
          var id = $(this).attr("data_id");
          $("#worklog-property input#choice-id").val(id);
          $('#selected-worklog-property').html($(this).text() + "");
          $('#worklog-property').removeClass('open');
          return false;
      })

      // it restarts the timer and then hides itself to show the pause button
      $play_button.bind('click', function() {
          self.last_start_point = new Date();

          self.timer = setInterval( function() { pulse.call(self) }, self.INTERVAL);
          $(this).hide();
          $pause_button.show();

          return false;
      });

      // drop-down elements behaviour
      $li_elapsed.click(function() {
          // Verificando se há categoria marcada
          if ($('#kind:checked').length == 1) {
              var minutes = Math.floor(self.total_milliseconds / 60000 ) % 60;
              var hours = Math.floor(self.total_milliseconds / 3600000);

              $('#taskform input[name="work_log[duration]"]').val(hours + 'h' + minutes + 'm');
              $form.submit();
          } else {
              alert('Escolha uma categoria');
          }
      });

      $li_none.click(function() {
          $('#taskform input[name="work_log[duration]"]').remove();
          from_dropdown = true;
          $form.submit();
      });

      // show dropdown
      $save_button.click(function() {
          var minutes = Math.floor(self.total_milliseconds / 60000 ) % 60;
          var hours = Math.floor(self.total_milliseconds / 3600000);

          var hour_unit = hours > 1 ? "hours" : "hour";
          var minute_unit = minutes > 1 ? "minutes" : "minute";

          // show elapsed in drop-down list
          if (hours > 0) {
              $('#worklog-elapsed > a').text(hours + " " + hour_unit + " " + minutes + " " + minute_unit);
          } else {
              $('#worklog-elapsed > a').text(minutes + " " + minute_unit);
          }

          return false;
      });

      $("button.save", $dialog).click(function(e) {
        // Verificando se há categoria marcada
        if ($('#kind:checked').length == 1) {
          $('input[name^="work_log"]', $form).remove();
          $dialog.modal('hide');
          $dialog.addClass("none").appendTo($form);
          $form.submit();
          return false;
        } else {
          alert('Escolha uma categoria');
        }
      })
  }

  function pulse() {
      var new_start_point = new Date();
      this.total_milliseconds += (new_start_point - this.last_start_point);
      this.last_start_point = new_start_point;

      var minutes = Math.floor(this.total_milliseconds / 60000 ) % 60;
      var hours = Math.floor(this.total_milliseconds / 3600000);
      var hour_unit = hours > 1 ? "hours" : "hours";
      var minute_unit = minutes > 1 ? "minutes" : "minute";

      jQuery('#hours .unit').text(hour_unit);
      jQuery('#minutes .unit').text(minute_unit);
      this.$minutes.text(minutes);
      this.$hours.text(hours);

      if (hours > 0) {
          jQuery('#hours').show();
      } else {
          jQuery('#hours').hide();
      }
  }

  function destroy() {
      clearInterval(this.timer);
  }

  function init() {
      this.INTERVAL = 60000; // runs every minute
      this.timer = null;
      this.total_milliseconds = 0;
      this.last_start_point = new Date();

      this.$minutes = jQuery('#minutes > .timer-val');
      this.$hours   = jQuery('#hours > .timer-val');
      jQuery('#hours').hide();

      // initial timer values
      this.$minutes.text('0');
      this.$hours.text('0');

  }

  function TaskTimer() {
      init.call(this);
      bind.call(this);

      //public methods
      this.destroy = destroy;

      // init timer
      var self = this;
      this.timer = setInterval(function(){ pulse.apply(self) }, this.INTERVAL);
  }

  return TaskTimer;
})();
<commit_msg>refatoração e melhoria de mensagem de erro<commit_after>var jobsworth = jobsworth || {}
jobsworth.tasks = jobsworth.tasks || {}

jobsworth.tasks.TaskTimer = (function(){

  function bind() {
      var $ = jQuery;
      var self = this;

      // buttons
      var $inline = $('#timer-bar-elapsed'),
          $pause_button = $('#pause-btn'),
          $play_button  = $('#play-btn'),
          $pin_button   = $('#pin-btn'),
          $save_button  = $('#save-btn'),
          $li_elapsed = $('#worklog-elapsed'),
          $li_custom = $('#worklog-custom'),
          $li_none = $('#worklog-none'),
          $dialog = $('#worktime-dialog'),
          $form = $('#taskform');

      // initialize modal
      $dialog.draggable({handle: '.modal-header'});

      // bind dropdown
      $('.dropdown-toggle').dropdown();

      // bindings
      $pause_button.bind('click', function() {
          self.total_milliseconds += (new Date() - self.last_start_point);

          clearInterval(self.timer);
          $(this).hide();
          $play_button.show();

          return false;
      });

      $('#worklog-property ul li').bind('click', function() {
          var id = $(this).attr("data_id");
          $("#worklog-property input#choice-id").val(id);
          $('#selected-worklog-property').html($(this).text() + "");
          $('#worklog-property').removeClass('open');
          return false;
      })

      // it restarts the timer and then hides itself to show the pause button
      $play_button.bind('click', function() {
          self.last_start_point = new Date();

          self.timer = setInterval( function() { pulse.call(self) }, self.INTERVAL);
          $(this).hide();
          $pause_button.show();

          return false;
      });

      // drop-down elements behaviour
      $li_elapsed.click(function() {
          // Verificando se há categoria marcada
          if (jQuery('#kind:checked').length == 1) {
              var minutes = Math.floor(self.total_milliseconds / 60000 ) % 60;
              var hours = Math.floor(self.total_milliseconds / 3600000);

              jQuery('#taskform input[name="work_log[duration]"]').val(hours + 'h' + minutes + 'm');
              $form.submit();
          } else {
              workLogKindCheckError();
          }
      });

      $li_none.click(function() {
          $('#taskform input[name="work_log[duration]"]').remove();
          from_dropdown = true;
          $form.submit();
      });

      // show dropdown
      $save_button.click(function() {
          var minutes = Math.floor(self.total_milliseconds / 60000 ) % 60;
          var hours = Math.floor(self.total_milliseconds / 3600000);

          var hour_unit = hours > 1 ? "hours" : "hour";
          var minute_unit = minutes > 1 ? "minutes" : "minute";

          // show elapsed in drop-down list
          if (hours > 0) {
              $('#worklog-elapsed > a').text(hours + " " + hour_unit + " " + minutes + " " + minute_unit);
          } else {
              $('#worklog-elapsed > a').text(minutes + " " + minute_unit);
          }

          return false;
      });

      $("button.save", $dialog).click(function(e) {
        // Verificando se há categoria marcada
        if (jQuery('#kind:checked').length == 1) {
          jQuery('input[name^="work_log"]', $form).remove();
          $dialog.modal('hide');
          $dialog.addClass("none").appendTo($form);
          $form.submit();
          return false;
        } else {
          workLogKindCheckError();
        }
      });
  }

  function pulse() {
      var new_start_point = new Date();
      this.total_milliseconds += (new_start_point - this.last_start_point);
      this.last_start_point = new_start_point;

      var minutes = Math.floor(this.total_milliseconds / 60000 ) % 60;
      var hours = Math.floor(this.total_milliseconds / 3600000);
      var hour_unit = hours > 1 ? "hours" : "hours";
      var minute_unit = minutes > 1 ? "minutes" : "minute";

      jQuery('#hours .unit').text(hour_unit);
      jQuery('#minutes .unit').text(minute_unit);
      this.$minutes.text(minutes);
      this.$hours.text(hours);

      if (hours > 0) {
          jQuery('#hours').show();
      } else {
          jQuery('#hours').hide();
      }
  }

  function destroy() {
      clearInterval(this.timer);
  }

  function init() {
      this.INTERVAL = 60000; // runs every minute
      this.timer = null;
      this.total_milliseconds = 0;
      this.last_start_point = new Date();

      this.$minutes = jQuery('#minutes > .timer-val');
      this.$hours   = jQuery('#hours > .timer-val');
      jQuery('#hours').hide();

      // initial timer values
      this.$minutes.text('0');
      this.$hours.text('0');

  }

  function workLogKindCheckError() {
    if (jQuery('#kind:checked').length == 0) {
      alert('Escolha uma categoria');
    }
    else {
      alert('Escolha apenas uma categoria');
    }
  }

  function TaskTimer() {
      init.call(this);
      bind.call(this);

      //public methods
      this.destroy = destroy;

      // init timer
      var self = this;
      this.timer = setInterval(function(){ pulse.apply(self) }, this.INTERVAL);
  }

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


<commit_before>function FavoritesAssistant() {
	/* this is the creator function for your scene assistant object. It will be passed all the
	   additional parameters (after the scene name) that were passed to pushScene. The reference
	   to the scene controller (this.controller) has not be established yet, so any initialization
	   that needs the scene controller should be done in the setup function below. */
	scene_helpers.addCommonSceneMethods(this);

	/*
		this connects App to this property of the appAssistant
	*/
	App = Spaz.getAppObj();
}
FavoritesAssistant.prototype.aboutToActivate = function(callback){
	callback.defer();
};
FavoritesAssistant.prototype.setup = function() {
	var thisA = this;

	this.scroller = this.controller.getSceneScroller();
	this.initAppMenu({ 'items':LOGGEDIN_APPMENU_ITEMS });
	this.initTwit('DOM');

	this.setupCommonMenus({
		viewMenuItems: [
			{
				items:[
					{label: $L("Favorites"), command:'scroll-top', 'class':"palm-header left", width:260},
					{label: $L('Compose'),  icon:'compose', command:'compose', shortcut:'N'}

				]
			}

		],
		cmdMenuItems: [
			{},
			{
				/*
					So we don't get the hard-to-see disabled look on the selected button,
					we make the current toggle command "IGNORE", which will not trigger an action
				*/
				toggleCmd:'favorites',
				items: [
					{label:$L('My Timeline'), icon:'conversation', command:'filter-timeline-all', shortcut:'T', 'class':"palm-header left"},
					{label:'@',	icon:'at', command:'filter-timeline-replies'},
					{label:$L('DM'), icon: 'dms', secondaryIconPath:'', command:'filter-timeline-dms'},
					{label:$L('Favorites'), iconPath:'images/theme/menu-icon-favorite.png', command:'favorites', shortcut:'F'},
					{label:$L('Search'),    icon:'search', command:'search', shortcut:'S'}
				]
			},
			{}
		]
	});

	/* this function is for setup tasks that have to happen when the scene is first created */

	/* use Luna.View.render to render view templates and add them to the scene, if needed. */

	/* setup widgets here */

	/* add event handlers to listen to events from widgets */

	this.setupInlineSpinner('activity-spinner-favorites');

	this.refreshOnActivate = true;

	this.controller.setupWidget("favorites-timeline",
        this.timeline_attributes = {
            itemTemplate: 'timeline-entry',
            listTemplate: 'timeline-container',
			emptyTemplate: 'timeline-container-empty',
            swipeToDelete: false,
            reorderable: false,
            hasNoWidgets: true,
			formatters: {
				'data': function(value, model) {
					return thisA.renderItem(value);
				}
			}
        },
        this.timeline_model = {
            items : []
        }
    );
	this.timeline_list = this.controller.get('favorites-timeline');

	/*
		more button
	*/
	jQuery('#more-favs-button').bind(Mojo.Event.tap, function() {
		thisA.loadMore.call(thisA);
	});
	this.moreButtonAttributes = {};
	this.moreButtonModel = {
		"buttonLabel" : "More",
		"buttonClass" : 'Primary'
	};
	this.controller.setupWidget('more-favs-button', this.moreButtonAttributes, this.moreButtonModel);

};

FavoritesAssistant.prototype.activate = function(event) {
	/* put in event handlers here that should only be in effect when this scene is active. For
	   example, key handlers that are observing the document */

	// this.addPostPopup();

	var thisA = this; // for closures below

	var tts = App.prefs.get('timeline-text-size');
	this.setTimelineTextSize('#favorites-timeline', tts);

	/*
		Prepare for timeline entry taps
	*/
	this.bindTimelineEntryTaps('favorites-timeline');

	/*
		start the favs timeline
	*/
	if (this.refreshOnActivate) {
		this.refresh();
		this.refreshOnActivate = false;
	}

};

FavoritesAssistant.prototype.deactivate = function(event) {
	/*
		stop listening for timeline entry taps
	*/
	this.unbindTimelineEntryTaps('favorites-timeline');

};

FavoritesAssistant.prototype.cleanup = function(event) {
	jQuery('#more-favs-button').unbind(Mojo.Event.tap);
};

FavoritesAssistant.prototype.getEntryElementByStatusId = function(id) {
	var el = jQuery('#favorites-timeline div.timeline-entry[data-status-id='+id+']', this.scroller).get(0);
	return el;
};

FavoritesAssistant.prototype.refresh = function(event) {
	var thisA = this;

	var page = 0;
	if (event && sch.isNumber(event)) {
		page = event;
	}

	sc.helpers.markAllAsRead('#favorites-timeline div.timeline-entry');
	this.showInlineSpinner('activity-spinner-favorites', $L('Loading favorites…'));

	/*
		reset scrollstate to avoid white flash
	*/
	var scrollstate = this.scroller.mojo.getState();
	this.scroller.mojo.setState(scrollstate, false);

	this.twit.getFavorites(
		page,
		null,
		function(data) {
			if (sch.isArray(data)) {

				data = data.reverse();
				var no_dupes = [];

				for (var i=0; i < data.length; i++) {

					/*
						only add if it doesn't already exist
					*/
					if (!thisA.itemExistsInModel(data[i])) {

						App.Tweets.save(data[i]);
						data[i].text = Spaz.makeItemsClickable(data[i].text);
						no_dupes.push(data[i]);
					}

				};

				thisA.addItems(no_dupes);

			}

			thisA.hideInlineSpinner('activity-spinner-favorites');
		},
		function(xhr, msg, exc) {
			Mojo.Log.error('EROROR in getFavorites');
			Mojo.Log.error("%j , %j , %j", xhr, msg, exc);

			var err_msg = $L("There was an error retrieving your favorites");
			thisA.displayErrorInfo(err_msg, null);

			/*
				Update relative dates
			*/
			thisA.hideInlineSpinner('activity-spinner-favorites');
		}
	);

};

FavoritesAssistant.prototype.loadMore = function(event) {
	if (this.faves_more_page) {
		this.faves_more_page++;
	} else {
		this.faves_more_page = 2;
	}

	this.refresh(this.faves_more_page);
};

/*
	redefine addItems to work with list model
*/
FavoritesAssistant.prototype.addItems = function(new_items) {

	// now we have all the existing items from the model
	var model_items = this.timeline_model.items.clone();

	var model_item;
	for (var i=0; i < new_items.length; i++) {
		model_item = {
			'id':new_items[i].id,
			'data':sch.clone(new_items[i])
		};
		// add each item to the model
		model_items.push(model_item);

	}

	// sort, in reverse
	model_items.sort(function(a,b){
		return b.id - a.id; // newest first
	});

	// re-assign the cloned items back to the model object
	this.timeline_model.items = model_items;

	// tell the controller it's changed to update list widget
	this.controller.modelChanged(this.timeline_model);

	/*
		reset scrollstate to avoid white flash
	*/
	var scrollstate = this.scroller.mojo.getState();
	this.scroller.mojo.setState(scrollstate, false);
};

FavoritesAssistant.prototype.renderItem = function(obj) {

    var html = '';

    Mojo.Timing.resume("timing_favorites-timeline.renderer");
	try {
		if (obj.SC_is_dm) {
			html = App.tpl.parseTemplate('dm', obj);
		} else {
			html = App.tpl.parseTemplate('tweet', obj);
		}
		Mojo.Timing.pause("timing_favorites-timeline.renderer");
		return html;

	} catch(err) {
		sch.error("There was an error rendering the object: "+sch.enJSON(obj));
		sch.error("Error:"+sch.enJSON(err));
		Mojo.Timing.pause("timing_favorites-timeline.renderer");

		return '';
	}

};

FavoritesAssistant.prototype.itemExistsInModel = function(obj) {

	for (var i=0; i < this.timeline_model.items.length; i++) {
		if (this.timeline_model.items[i].id == obj.id) {
			sch.error(obj.id +' exists in model');
			return true;
		}
	}
	sch.error(obj.id +' does not exist in model');
	return false;
};

/**
 * oh my god this is probably deadly for performance
 */
FavoritesAssistant.prototype.markAllAsRead = function() {
	for (var i=0; i < this.timeline_model.items.length; i++) {
		var new_element = jQuery(this.timeline_model.items[i].html).removeClass('new').get(0);
		if (new_element) {
			this.timeline_model.items[i].html = new_element.outerHTML;
		}
	}
	this.controller.modelChanged(this.timeline_model);
};<commit_msg>Added friends and followers button to cmd menu<commit_after>function FavoritesAssistant() {
	/* this is the creator function for your scene assistant object. It will be passed all the
	   additional parameters (after the scene name) that were passed to pushScene. The reference
	   to the scene controller (this.controller) has not be established yet, so any initialization
	   that needs the scene controller should be done in the setup function below. */
	scene_helpers.addCommonSceneMethods(this);

	/*
		this connects App to this property of the appAssistant
	*/
	App = Spaz.getAppObj();
}
FavoritesAssistant.prototype.aboutToActivate = function(callback){
	callback.defer();
};
FavoritesAssistant.prototype.setup = function() {
	var thisA = this;

	this.scroller = this.controller.getSceneScroller();
	this.initAppMenu({ 'items':LOGGEDIN_APPMENU_ITEMS });
	this.initTwit('DOM');

	this.setupCommonMenus({
		viewMenuItems: [
			{
				items:[
					{label: $L("Favorites"), command:'scroll-top', 'class':"palm-header left", width:260},
					{label: $L('Compose'),  icon:'compose', command:'compose', shortcut:'N'}

				]
			}

		],
		cmdMenuItems: [
			{},
			{
				/*
					So we don't get the hard-to-see disabled look on the selected button,
					we make the current toggle command "IGNORE", which will not trigger an action
				*/
				toggleCmd:'favorites',
				items: [
					{label:$L('My Timeline'), icon:'conversation', command:'filter-timeline-all', shortcut:'T', 'class':"palm-header left"},
					{label:'@',	icon:'at', command:'filter-timeline-replies'},
					{label:$L('DM'), icon: 'dms', secondaryIconPath:'', command:'filter-timeline-dms'},
					{label:$L('Favorites'), iconPath:'images/theme/menu-icon-favorite.png', command:'favorites', shortcut:'F'},
					{label:$L('Friends and Followers'), iconPath:'images/theme/menu-icon-friends-followers.png', command:'friends-followers', shortcut:'L'},
					{label:$L('Search'),    icon:'search', command:'search', shortcut:'S'}
				]
			},
			{}
		]
	});

	/* this function is for setup tasks that have to happen when the scene is first created */

	/* use Luna.View.render to render view templates and add them to the scene, if needed. */

	/* setup widgets here */

	/* add event handlers to listen to events from widgets */

	this.setupInlineSpinner('activity-spinner-favorites');

	this.refreshOnActivate = true;

	this.controller.setupWidget("favorites-timeline",
        this.timeline_attributes = {
            itemTemplate: 'timeline-entry',
            listTemplate: 'timeline-container',
			emptyTemplate: 'timeline-container-empty',
            swipeToDelete: false,
            reorderable: false,
            hasNoWidgets: true,
			formatters: {
				'data': function(value, model) {
					return thisA.renderItem(value);
				}
			}
        },
        this.timeline_model = {
            items : []
        }
    );
	this.timeline_list = this.controller.get('favorites-timeline');

	/*
		more button
	*/
	jQuery('#more-favs-button').bind(Mojo.Event.tap, function() {
		thisA.loadMore.call(thisA);
	});
	this.moreButtonAttributes = {};
	this.moreButtonModel = {
		"buttonLabel" : "More",
		"buttonClass" : 'Primary'
	};
	this.controller.setupWidget('more-favs-button', this.moreButtonAttributes, this.moreButtonModel);

};

FavoritesAssistant.prototype.activate = function(event) {
	/* put in event handlers here that should only be in effect when this scene is active. For
	   example, key handlers that are observing the document */

	// this.addPostPopup();

	var thisA = this; // for closures below

	var tts = App.prefs.get('timeline-text-size');
	this.setTimelineTextSize('#favorites-timeline', tts);

	/*
		Prepare for timeline entry taps
	*/
	this.bindTimelineEntryTaps('favorites-timeline');

	/*
		start the favs timeline
	*/
	if (this.refreshOnActivate) {
		this.refresh();
		this.refreshOnActivate = false;
	}

};

FavoritesAssistant.prototype.deactivate = function(event) {
	/*
		stop listening for timeline entry taps
	*/
	this.unbindTimelineEntryTaps('favorites-timeline');

};

FavoritesAssistant.prototype.cleanup = function(event) {
	jQuery('#more-favs-button').unbind(Mojo.Event.tap);
};

FavoritesAssistant.prototype.getEntryElementByStatusId = function(id) {
	var el = jQuery('#favorites-timeline div.timeline-entry[data-status-id='+id+']', this.scroller).get(0);
	return el;
};

FavoritesAssistant.prototype.refresh = function(event) {
	var thisA = this;

	var page = 0;
	if (event && sch.isNumber(event)) {
		page = event;
	}

	sc.helpers.markAllAsRead('#favorites-timeline div.timeline-entry');
	this.showInlineSpinner('activity-spinner-favorites', $L('Loading favorites…'));

	/*
		reset scrollstate to avoid white flash
	*/
	var scrollstate = this.scroller.mojo.getState();
	this.scroller.mojo.setState(scrollstate, false);

	this.twit.getFavorites(
		page,
		null,
		function(data) {
			if (sch.isArray(data)) {

				data = data.reverse();
				var no_dupes = [];

				for (var i=0; i < data.length; i++) {

					/*
						only add if it doesn't already exist
					*/
					if (!thisA.itemExistsInModel(data[i])) {

						App.Tweets.save(data[i]);
						data[i].text = Spaz.makeItemsClickable(data[i].text);
						no_dupes.push(data[i]);
					}

				};

				thisA.addItems(no_dupes);

			}

			thisA.hideInlineSpinner('activity-spinner-favorites');
		},
		function(xhr, msg, exc) {
			Mojo.Log.error('EROROR in getFavorites');
			Mojo.Log.error("%j , %j , %j", xhr, msg, exc);

			var err_msg = $L("There was an error retrieving your favorites");
			thisA.displayErrorInfo(err_msg, null);

			/*
				Update relative dates
			*/
			thisA.hideInlineSpinner('activity-spinner-favorites');
		}
	);

};

FavoritesAssistant.prototype.loadMore = function(event) {
	if (this.faves_more_page) {
		this.faves_more_page++;
	} else {
		this.faves_more_page = 2;
	}

	this.refresh(this.faves_more_page);
};

/*
	redefine addItems to work with list model
*/
FavoritesAssistant.prototype.addItems = function(new_items) {

	// now we have all the existing items from the model
	var model_items = this.timeline_model.items.clone();

	var model_item;
	for (var i=0; i < new_items.length; i++) {
		model_item = {
			'id':new_items[i].id,
			'data':sch.clone(new_items[i])
		};
		// add each item to the model
		model_items.push(model_item);

	}

	// sort, in reverse
	model_items.sort(function(a,b){
		return b.id - a.id; // newest first
	});

	// re-assign the cloned items back to the model object
	this.timeline_model.items = model_items;

	// tell the controller it's changed to update list widget
	this.controller.modelChanged(this.timeline_model);

	/*
		reset scrollstate to avoid white flash
	*/
	var scrollstate = this.scroller.mojo.getState();
	this.scroller.mojo.setState(scrollstate, false);
};

FavoritesAssistant.prototype.renderItem = function(obj) {

    var html = '';

    Mojo.Timing.resume("timing_favorites-timeline.renderer");
	try {
		if (obj.SC_is_dm) {
			html = App.tpl.parseTemplate('dm', obj);
		} else {
			html = App.tpl.parseTemplate('tweet', obj);
		}
		Mojo.Timing.pause("timing_favorites-timeline.renderer");
		return html;

	} catch(err) {
		sch.error("There was an error rendering the object: "+sch.enJSON(obj));
		sch.error("Error:"+sch.enJSON(err));
		Mojo.Timing.pause("timing_favorites-timeline.renderer");

		return '';
	}

};

FavoritesAssistant.prototype.itemExistsInModel = function(obj) {

	for (var i=0; i < this.timeline_model.items.length; i++) {
		if (this.timeline_model.items[i].id == obj.id) {
			sch.error(obj.id +' exists in model');
			return true;
		}
	}
	sch.error(obj.id +' does not exist in model');
	return false;
};

/**
 * oh my god this is probably deadly for performance
 */
FavoritesAssistant.prototype.markAllAsRead = function() {
	for (var i=0; i < this.timeline_model.items.length; i++) {
		var new_element = jQuery(this.timeline_model.items[i].html).removeClass('new').get(0);
		if (new_element) {
			this.timeline_model.items[i].html = new_element.outerHTML;
		}
	}
	this.controller.modelChanged(this.timeline_model);
};<|endoftext|>


<commit_before>'use strict';

angular.module('app.calendar', [])
  .component('calendarView', {
    controllerAs: 'calendar',
    bindings: {},
    controller: CalendarViewController,
    templateUrl: 'calendar-view/calendar.html'
  });

CalendarViewController.$inject = ['$scope',
  'uiCalendarConfig',
  'CourseRegistrationService',
  '$window',
  'CourseDataService'];
function CalendarViewController($scope,
                                uiCalendarConfig,
                                CourseRegistrationService,
                                $window,
                                CourseDataService) {

  var vm = this;

  vm.$onInit = function () {
    vm.colors = {};
    vm.courses = CourseRegistrationService.courses;
    vm.prevCourses = CourseRegistrationService.courses;
    /* CRN ADD*/
    vm.CRNInputs = [];
    vm.numCRNInput = 5;
    vm.dow = {'M': 1, 'T': 2, 'W': 3, 'R': 4, 'F': 5};
    vm.timeFormat = ['h:mm A'];
    var bgA = 0.4;
    var textA = 0.65;
    vm.randomColors = [{
      'background': 'rgb(233,127,127)',
      'backgroundA': 'rgba(233,127,127,' + bgA + ')',
      'text': 'rgb(42,85,71)',
      'textA': 'rgba(42,85,71,' + textA + ')'
    },
      {
        'background': 'rgb(126,131,233)',
        'backgroundA': 'rgba(126,131,233,' + bgA + ')',
        'text': 'rgb(224,255,255)',
        'textA': 'rgba(224,255,255,' + textA + ')'
      },
      {
        'background': 'rgb(240,158,70)',
        'backgroundA': 'rgba(240,158,70,' + bgA + ')',
        'text': 'rgb(42,42,42)',
        'textA': 'rgba(42,42,42,' + textA + ')'
      },
      // {
      //  'background': 'rgb(228,209,103)',
      //  'backgroundA': 'rgba(228,209,103,' + bgA + ')',
      //  'text': 'rgb(136,89,182)',
      //  'textA': 'rgba(136,89,182,' + textA + ')'
      // },
      {
        'background': 'rgb(103,228,123)',
        'backgroundA': 'rgba(103,228,123,' + bgA + ')',
        'text': 'rgb(0,122,124)',
        'textA': 'rgba(0,122,124,' + textA + ')'
      },
      {
        'background': 'rgb(143,103,228)',
        'backgroundA': 'rgba(143,103,228,' + bgA + ')',
        'text': 'rgb(200,247,197)',
        'textA': 'rgba(200,247,197,' + textA + ')'
      }];

    vm.previewColor = {'background': 'rgba(228,209,103,' + bgA + ')', 'text': 'rgba(136,89,182,' + textA + ')'};

    initCalendar();
    setupCRNInputs();
    setupRenderHandler();
    renderCalendar();
  };

  // Calendar represents running weeks.
  // Display a generic week by displaying course times relative to zeroTime.
  function getZeroTime() {
    // time 0, add a day to get a Monday.
    //noinspection JSPotentiallyInvalidConstructorUsage
    return new moment(0).startOf('week').add(1, 'd');
  }

  function setupRenderHandler() {
    // Courses added or dropped. Re-render calendar
    CourseRegistrationService.subscribe($scope, renderCalendar);
  }

  function renderCalendar() {
    // map objects into displayable calendar events
    var events = [];
    for (var crn in vm.courses) {
      if (vm.courses.hasOwnProperty(crn)) {
        // TODO: fix duplicates/reloading concern
        var course = vm.courses[crn];
        var transparent = course.preview;
        console.log(course);
        var colorPair = getColorForCRN(crn, transparent);
        var allDay = false;
        var unalteredStart = moment(course.meetings[0].start, vm.timeFormat);
        var end = moment(course.meetings[0].end, vm.timeFormat);
        var start = unalteredStart.clone();
        // check if start is TBD -- TODO:: allDay not working for now
        if (course.meetings[0].start === 'ARRANGED') {
          allDay = true;
          start = getZeroTime();
        } else {
          start = getZeroTime().add(start.get('h'), 'h').add(start.get('m'), 'm');
        }
        var duration = end.diff(unalteredStart);
        var className = duration < moment.duration(1, 'hours') ? 'calendar-event-hour' : '';
        var dow = [1]; // TBD classes show on Monday
        if (!allDay) {
          dow = getDow(course.meetings[0].daysOfTheWeek);
        }

        events.push({
          'id': crn,
          'title': courseToString(crn, course),
          'start': start,
          'end': getZeroTime().add(end.get('h'), 'h').add(end.get('m'), 'm'),
          'dow': dow,
          'color': colorPair.background,
          'textColor': colorPair.text,
          'overlap': false,
          'className': className,
          'allDay': allDay
        });
      }
    }

    //  ({{crn}}) - {{course.creditHours}}

    //  {{course.meetings[0].type.code}} {{course.sectionNumber}}

    //  {{course.meetings[0].instructors[0].name}}

    //  {{course.meetings[0].daysOfTheWeek}} {{course.meetings[0].start}} - {{course.meetings[0].end}}

    //  {{course.meetings[0].buildingName}} {{course.meetings[0].roomNumber}}

    // fullcalendar docs say this should be an array. On the contrary, only works as an object
    var eventSources = {
      'events': events,
      'className': 'calendar-event-content',
      'overlap': false
    };

    // dumb, necessary, unfortunate calendar hacks to re-render events
    angular.element('#calendar').fullCalendar('removeEvents');
    angular.element('#calendar').fullCalendar('addEventSource', eventSources);
    angular.element('#calendar').fullCalendar('rerenderEvents');
  }

  function courseToString(crn, course) {
    return [course.parents.subject.id,
      course.parents.course.id,
      course.parents.course.label,
      '(' + crn + ')'].join(' ');
  }

  function getColorForCRN(crn, transparent) {
    if (transparent) {
      return {
        bg: vm.previewColor.background,
        text: vm.previewColor.text
      };
    }

    if (!vm.colors[crn]) {
      vm.colors[crn] = getRandomColor();
    }

    return vm.colors[crn];
  }

  function getRandomColor() {
    var rand = Math.floor(Math.random() * (vm.randomColors.length));
    var bg = vm.randomColors[rand].background;
    var text = vm.randomColors[rand].text;
    return {
      bg: bg,
      text: text
    };
  }

  function getDow(weekdayLetters) {
    var dowArray = [];
    var i = 0;
    var len = weekdayLetters.length;
    for (; i < len; i++) {
      // vm.dow has weekday letters : numbers. Populate dowArray with numbers repre. days
      dowArray.push(vm.dow[weekdayLetters[i]]);
    }
    return dowArray;
  }

  function initCalendar() {
    $scope.uiConfig = {
      'calendar': {
        'height': 'auto',
        'editable': false,
        //'firstDay': 1, // Monday
        'weekends': false, // No weekend registration for now
        'defaultView': 'agendaWeek',
        'views': {
          'agendaWeek': {
            // EXTRA: these could be user config through a settings page.
            'allDayText': 'TBD',
            'minTime': '08:00:00',
            'maxTime': '19:00:00',
            'slotDuration': '00:15:00',
            //'slotDuration': '00:30:00',
            'slotLabelInterval': '01:00:00',
            'slotEventOverlap': false,
            'columnFormat': 'dddd'
          }
        },
        // use defaultDate unix:0 time, add courses based on 1st week unix:0 time
        'defaultDate': getZeroTime(),
        'header': false,
        'overlap': false
      }
    };
  }

  function setupCRNInputs() {
    var i = 0;
    for (; i < vm.numCRNInput; i++) {
      var input = {'crn': ''};
      vm.CRNInputs.push(input);
    }
  }

  vm.registerByCRN = function () {
    var needsRender = false;
    vm.CRNInputs.forEach(function (userInput) {
      var crn = userInput.crn;
      var section = CourseDataService.section(userInput.crn);
      // valid crn and not already registered -- check if preview
      if (section) {
        if (!vm.courses[crn] || (vm.courses[crn] && vm.courses[crn].preview)) {
          if (vm.courses[crn]) {
            vm.courses[crn].preview = !vm.courses[crn].preview; // must be currently previewed
          } else {
            section.preview = false;
            CourseRegistrationService.addCourse(section); // add from overall
          }

          needsRender = true;
        }
      }
    });

    if (needsRender) {
      CourseRegistrationService.notifyChange();
    }
  };

  vm.registerAllPreview = function () {
    for (var crn in vm.courses) {
      if (vm.courses.hasOwnProperty(crn)) {
        var preview = vm.courses[crn].preview;
        if (preview) {
          vm.courses[crn].preview = !preview;
        }
      }
    }
    CourseRegistrationService.notifyChange();
  };

}
<commit_msg>Down with angular.element<commit_after>'use strict';

angular.module('app.calendar', [])
  .component('calendarView', {
    controllerAs: 'calendar',
    bindings: {},
    controller: CalendarViewController,
    templateUrl: 'calendar-view/calendar.html'
  });

CalendarViewController.$inject = ['$scope',
  'uiCalendarConfig',
  'CourseRegistrationService',
  '$window',
  'CourseDataService'];
function CalendarViewController($scope,
                                uiCalendarConfig,
                                CourseRegistrationService,
                                $window,
                                CourseDataService) {

  var vm = this;

  vm.$onInit = function () {
    vm.colors = {};
    vm.courses = CourseRegistrationService.courses;
    vm.prevCourses = CourseRegistrationService.courses;
    /* CRN ADD*/
    vm.CRNInputs = [];
    vm.numCRNInput = 5;
    vm.dow = {'M': 1, 'T': 2, 'W': 3, 'R': 4, 'F': 5};
    vm.timeFormat = ['h:mm A'];
    var bgA = 0.4;
    var textA = 0.65;
    vm.randomColors = [{
      'background': 'rgb(233,127,127)',
      'backgroundA': 'rgba(233,127,127,' + bgA + ')',
      'text': 'rgb(42,85,71)',
      'textA': 'rgba(42,85,71,' + textA + ')'
    },
      {
        'background': 'rgb(126,131,233)',
        'backgroundA': 'rgba(126,131,233,' + bgA + ')',
        'text': 'rgb(224,255,255)',
        'textA': 'rgba(224,255,255,' + textA + ')'
      },
      {
        'background': 'rgb(240,158,70)',
        'backgroundA': 'rgba(240,158,70,' + bgA + ')',
        'text': 'rgb(42,42,42)',
        'textA': 'rgba(42,42,42,' + textA + ')'
      },
      // {
      //  'background': 'rgb(228,209,103)',
      //  'backgroundA': 'rgba(228,209,103,' + bgA + ')',
      //  'text': 'rgb(136,89,182)',
      //  'textA': 'rgba(136,89,182,' + textA + ')'
      // },
      {
        'background': 'rgb(103,228,123)',
        'backgroundA': 'rgba(103,228,123,' + bgA + ')',
        'text': 'rgb(0,122,124)',
        'textA': 'rgba(0,122,124,' + textA + ')'
      },
      {
        'background': 'rgb(143,103,228)',
        'backgroundA': 'rgba(143,103,228,' + bgA + ')',
        'text': 'rgb(200,247,197)',
        'textA': 'rgba(200,247,197,' + textA + ')'
      }];

    vm.previewColor = {'background': 'rgba(228,209,103,' + bgA + ')', 'text': 'rgba(136,89,182,' + textA + ')'};

    initCalendar();
    setupCRNInputs();
    setupRenderHandler();
    renderCalendar();
  };

  // Calendar represents running weeks.
  // Display a generic week by displaying course times relative to zeroTime.
  function getZeroTime() {
    // time 0, add a day to get a Monday.
    //noinspection JSPotentiallyInvalidConstructorUsage
    return new moment(0).startOf('week').add(1, 'd');
  }

  function setupRenderHandler() {
    // Courses added or dropped. Re-render calendar
    CourseRegistrationService.subscribe($scope, renderCalendar);
  }

  function renderCalendar() {
    // map objects into displayable calendar events
    var events = [];
    for (var crn in vm.courses) {
      console.log(crn);
      if (vm.courses.hasOwnProperty(crn)) {
        console.log(crn);
        // TODO: fix duplicates/reloading concern
        var course = vm.courses[crn];
        console.log(course);
        var transparent = course.preview;
        console.log(course);
        var colorPair = getColorForCRN(crn, transparent);
        var allDay = false;
        var unalteredStart = moment(course.meetings[0].start, vm.timeFormat);
        var end = moment(course.meetings[0].end, vm.timeFormat);
        var start = unalteredStart.clone();
        // check if start is TBD -- TODO:: allDay not working for now
        if (course.meetings[0].start === 'ARRANGED') {
          allDay = true;
          start = getZeroTime();
        } else {
          start = getZeroTime().add(start.get('h'), 'h').add(start.get('m'), 'm');
        }
        var duration = end.diff(unalteredStart);
        var className = duration < moment.duration(1, 'hours') ? 'calendar-event-hour' : '';
        var dow = [1]; // TBD classes show on Monday
        if (!allDay) {
          dow = getDow(course.meetings[0].daysOfTheWeek);
        }

        events.push({
          'id': crn,
          'title': courseToString(crn, course),
          'start': start,
          'end': getZeroTime().add(end.get('h'), 'h').add(end.get('m'), 'm'),
          'dow': dow,
          'color': colorPair.background,
          'textColor': colorPair.text,
          'overlap': false,
          'className': className,
          'allDay': allDay
        });
      }
    }

    //  ({{crn}}) - {{course.creditHours}}

    //  {{course.meetings[0].type.code}} {{course.sectionNumber}}

    //  {{course.meetings[0].instructors[0].name}}

    //  {{course.meetings[0].daysOfTheWeek}} {{course.meetings[0].start}} - {{course.meetings[0].end}}

    //  {{course.meetings[0].buildingName}} {{course.meetings[0].roomNumber}}

    // fullcalendar docs say this should be an array. On the contrary, only works as an object
    var eventSources = {
      'events': events,
      'className': 'calendar-event-content',
      'overlap': false
    };

    console.log(eventSources);

    // dumb, necessary, unfortunate calendar hacks to re-render events
    uiCalendarConfig.calendars.schedule.fullCalendar('removeEvents');
    uiCalendarConfig.calendars.schedule.fullCalendar('addEventSource', eventSources);
    uiCalendarConfig.calendars.schedule.fullCalendar('rerenderEvents');
  }

  function courseToString(crn, course) {
    return [course.parents.subject.id,
      course.parents.course.id,
      course.parents.course.label,
      '(' + crn + ')'].join(' ');
  }

  function getColorForCRN(crn, transparent) {
    if (transparent) {
      return {
        bg: vm.previewColor.background,
        text: vm.previewColor.text
      };
    }

    if (!vm.colors[crn]) {
      vm.colors[crn] = getRandomColor();
    }

    return vm.colors[crn];
  }

  function getRandomColor() {
    var rand = Math.floor(Math.random() * (vm.randomColors.length));
    var bg = vm.randomColors[rand].background;
    var text = vm.randomColors[rand].text;
    return {
      bg: bg,
      text: text
    };
  }

  function getDow(weekdayLetters) {
    var dowArray = [];
    var i = 0;
    var len = weekdayLetters.length;
    for (; i < len; i++) {
      // vm.dow has weekday letters : numbers. Populate dowArray with numbers repre. days
      dowArray.push(vm.dow[weekdayLetters[i]]);
    }
    return dowArray;
  }

  function initCalendar() {
    $scope.uiConfig = {
      'calendar': {
        'height': 'auto',
        'editable': false,
        //'firstDay': 1, // Monday
        'weekends': false, // No weekend registration for now
        'defaultView': 'agendaWeek',
        'views': {
          'agendaWeek': {
            // EXTRA: these could be user config through a settings page.
            'allDayText': 'TBD',
            'minTime': '08:00:00',
            'maxTime': '19:00:00',
            'slotDuration': '00:15:00',
            //'slotDuration': '00:30:00',
            'slotLabelInterval': '01:00:00',
            'slotEventOverlap': false,
            'columnFormat': 'dddd'
          }
        },
        // use defaultDate unix:0 time, add courses based on 1st week unix:0 time
        'defaultDate': getZeroTime(),
        'header': false,
        'overlap': false
      }
    };
  }

  function setupCRNInputs() {
    var i = 0;
    for (; i < vm.numCRNInput; i++) {
      var input = {'crn': ''};
      vm.CRNInputs.push(input);
    }
  }

  vm.registerByCRN = function () {
    var needsRender = false;
    vm.CRNInputs.forEach(function (userInput) {
      var crn = userInput.crn;
      var section = CourseDataService.section(userInput.crn);
      // valid crn and not already registered -- check if preview
      if (section) {
        if (!vm.courses[crn] || (vm.courses[crn] && vm.courses[crn].preview)) {
          if (vm.courses[crn]) {
            vm.courses[crn].preview = !vm.courses[crn].preview; // must be currently previewed
          } else {
            section.preview = false;
            CourseRegistrationService.addCourse(section); // add from overall
          }

          needsRender = true;
        }
      }
    });

    if (needsRender) {
      CourseRegistrationService.notifyChange();
    }
  };

  vm.registerAllPreview = function () {
    for (var crn in vm.courses) {
      if (vm.courses.hasOwnProperty(crn)) {
        var preview = vm.courses[crn].preview;
        if (preview) {
          vm.courses[crn].preview = !preview;
        }
      }
    }
    CourseRegistrationService.notifyChange();
  };

}
<|endoftext|>


<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React, { useState } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import uuidv1 from 'uuid';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Divider from '@material-ui/core/Divider';
// import ContentExtractionIcon from '@material-ui/icons/TrackChanges';
import OpenFolderIcon from '@material-ui/icons/SubdirectoryArrowLeft';
import AddExistingFileIcon from '@material-ui/icons/ExitToApp';
import OpenFolderNativelyIcon from '@material-ui/icons/Launch';
import AutoRenew from '@material-ui/icons/Autorenew';
import DefaultPerspectiveIcon from '@material-ui/icons/GridOn';
import GalleryPerspectiveIcon from '@material-ui/icons/Camera';
import MapiquePerspectiveIcon from '@material-ui/icons/Map';
import TreeVizPerspectiveIcon from '@material-ui/icons/AccountTree';
import NewFileIcon from '@material-ui/icons/InsertDriveFile';
import NewFolderIcon from '@material-ui/icons/CreateNewFolder';
import RenameFolderIcon from '@material-ui/icons/FormatTextdirectionLToR';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import SettingsIcon from '@material-ui/icons/Settings';
import ConfirmDialog from '../dialogs/ConfirmDialog';
import CreateDirectoryDialog from '../dialogs/CreateDirectoryDialog';
import RenameDirectoryDialog from '../dialogs/RenameDirectoryDialog';
import AppConfig from '../../config';
import i18n from '../../services/i18n';
import { extractFileName, normalizePath } from '../../utils/paths'; // extractFileExtension
import PlatformIO from '../../services/platform-io';
import { formatDateTime4Tag } from '../../utils/misc';
import { actions as AppActions } from '../../reducers/app';
import IOActions from '../../reducers/io-actions';

type Props = {
  open: boolean,
  onClose: () => void,
  anchorEl: Object | null,
  directoryPath: string,
  loadDirectoryContent: (path: string) => void,
  openDirectory: (path: string) => void,
  openFile: (path: string, isFile: boolean) => void,
  deleteDirectory: (path: string) => void,
  reflectCreateEntry: (path: string, isFile: boolean) => void,
  toggleCreateFileDialog: () => void,
  // extractContent: (config: Object) => void,
  switchPerspective: (perspectiveId: string) => void,
  perspectiveMode: boolean,
  showNotification: (
    text: string,
    notificationType: string,
    autohide: boolean
  ) => void,
  isReadOnlyMode: boolean
};

const DirectoryMenu = (props: Props) => {
  let fileInput; // Object | null;

  const [isCreateDirectoryDialogOpened, setIsCreateDirectoryDialogOpened] = useState(false);
  const [isDeleteDirectoryDialogOpened, setIsDeleteDirectoryDialogOpened] = useState(false);
  const [isRenameDirectoryDialogOpened, setIsRenameDirectoryDialogOpened] = useState(false);

  function reloadDirectory() {
    props.onClose();
    props.loadDirectoryContent(props.directoryPath);
  }

  function openDirectory() {
    props.onClose();
    props.loadDirectoryContent(props.directoryPath);
  }

  function showProperties() {
    props.onClose();
    props.openFile(props.directoryPath, false);
  }

  // function initContentExtraction() {
  //   props.onClose();
  //   props.extractContent({ EXIFGeo: true });
  // }

  function switchPerspective(perspectiveId) {
    props.onClose();
    props.switchPerspective(perspectiveId);
  }

  function showDeleteDirectoryDialog() {
    props.onClose();
    setIsDeleteDirectoryDialogOpened(true);
  }

  function showRenameDirectoryDialog() {
    props.onClose();
    setIsRenameDirectoryDialogOpened(true);
  }

  function showCreateDirectoryDialog() {
    props.onClose();
    setIsCreateDirectoryDialogOpened(true);
  }

  function createNewFile() {
    props.onClose();
    props.toggleCreateFileDialog();
  }

  function handleCloseDialogs() {
    setIsCreateDirectoryDialogOpened(false);
    setIsDeleteDirectoryDialogOpened(false);
    setIsRenameDirectoryDialogOpened(false);
  }

  function showInFileManager() {
    props.onClose();
    props.openDirectory(props.directoryPath);
  }

  function addExistingFile() {
    props.onClose();
    fileInput.click();
  }

  function onFail(message) {
    console.log('Camera Failed: ' + message);
  }

  function onCameraSuccess(imageURL) {
    window.resolveLocalFileSystemURL(imageURL, (fp) => {
      moveFile(fp.nativeURL);
    }, () => { console.log('Failed to get filesystem url'); });
  }

  function moveFile(filePath) {
    const fileName = 'IMG_TS' + AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + '.jpg';
    const newFilePath =
      normalizePath(props.directoryPath) +
      AppConfig.dirSeparator +
      fileName;

    PlatformIO.renameFilePromise(filePath, newFilePath)
      .then(() => {
        props.showNotification(
          'File ' + newFilePath + ' successfully imported.',
          'default',
          true
        );
        props.reflectCreateEntry(newFilePath, true);
        return true;
      })
      .catch(error => {
        // TODO showAlertDialog("Saving " + filePath + " failed.");
        console.error('Save to file ' + newFilePath + ' failed ' + error);
        props.showNotification(
          'Importing file ' + newFilePath + ' failed.',
          'error',
          true
        );
        return true;
      });
  }

  // function loadImageLocal() {
  //   props.onClose();
  //   navigator.camera.getPicture(onCameraSuccess, onFail, {
  //     destinationType: Camera.DestinationType.FILE_URI,
  //     sourceType: Camera.PictureSourceType.PHOTOLIBRARY
  //   });
  // }

  function cameraTakePicture() {
    props.onClose();
    navigator.camera.getPicture(onCameraSuccess, onFail, {
      // quality: 50,
      destinationType: Camera.DestinationType.FILE_URI, // DATA_URL, // Return base64 encoded string
      // encodingType: Camera.EncodingType.JPEG,
      mediaType: Camera.MediaType.PICTURE // ALLMEDIA
    });
  }

  function handleFileInputChange(selection: Object) {
    // console.log("Selected File: "+JSON.stringify(selection.currentTarget.files[0]));
    const file = selection.currentTarget.files[0];
    const filePath =
      normalizePath(props.directoryPath) +
      AppConfig.dirSeparator +
      decodeURIComponent(file.name);

    const reader = new FileReader();
    reader.onload = event => {
      // console.log('Content on file read complete: ' + JSON.stringify(event));
      // change name for ios fakepath
      // if (AppConfig.isCordovaiOS) {
      //   const fileExt = extractFileExtension(addFileInputName);
      //   addFileInputName = AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + fileExt;
      // }
      // TODO event.currentTarget.result is ArrayBuffer
      // Sample call from PRO version using content = Utils.base64ToArrayBuffer(baseString);
      PlatformIO.getPropertiesPromise(filePath).then((entryProps) => {
        if (entryProps) {
          props.showNotification(
            'File with the same name already exist, importing skipped!',
            'warning',
            true
          );
        } else {
          PlatformIO.saveBinaryFilePromise(
            filePath,
            event.currentTarget.result,
            true
          )
            .then(() => {
              props.showNotification(
                'File ' + filePath + ' successfully imported.',
                'default',
                true
              );
              props.reflectCreateEntry(filePath, true);
              return true;
            })
            .catch(error => {
              // TODO showAlertDialog("Saving " + filePath + " failed.");
              console.error('Save to file ' + filePath + ' failed ' + error);
              props.showNotification(
                'Importing file ' + filePath + ' failed.',
                'error',
                true
              );
              return true;
            });
        }
        return true;
      }).catch((err) => {
        console.log('Error getting properties ' + err);
      });
    };

    if (AppConfig.isCordova) {
      reader.readAsDataURL(file);
    } else {
      reader.readAsArrayBuffer(file);
    }
  }

  return (

      <RenameDirectoryDialog
        key={uuidv1()}
        open={isRenameDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        selectedDirectoryPath={props.directoryPath}
      />
      <CreateDirectoryDialog
        key={uuidv1()}
        open={isCreateDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        selectedDirectoryPath={props.directoryPath}
      />
      <ConfirmDialog
        open={isDeleteDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        title={i18n.t('core:deleteDirectoryTitleConfirm')}
        content={i18n.t('core:deleteDirectoryContentConfirm', {
          dirPath: props.directoryPath
            ? extractFileName(props.directoryPath)
            : ''
        })}
        confirmCallback={result => {
          if (result) {
            props.deleteDirectory(props.directoryPath);
          }
        }}
        confirmDialogContent={'confirmDialogContent'}
        cancelDialogTID={'cancelDeleteDirectoryDialog'}
        confirmDialogTID={'confirmDeleteDirectoryDialog'}
      />
      <Menu
        anchorEl={props.anchorEl}
        open={props.open}
        onClose={props.onClose}
      >
        {props.perspectiveMode && (
          <MenuItem
            data-tid="openDirectory"
            onClick={openDirectory}
          >
            <ListItemIcon>
              <OpenFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:openDirectory')} />
          </MenuItem>
        )}
        {!props.perspectiveMode && (
          <MenuItem data-tid="reloadDirectory" onClick={reloadDirectory}>
            <ListItemIcon>
              <AutoRenew />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:reloadDirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && (
          <MenuItem
            data-tid="renameDirectory"
            onClick={showRenameDirectoryDialog}
          >
            <ListItemIcon>
              <RenameFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:renameDirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && (
          <MenuItem
            data-tid="deleteDirectory"
            onClick={showDeleteDirectoryDialog}
          >
            <ListItemIcon>
              <DeleteForeverIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:deleteDirectory')} />
          </MenuItem>
        )}
        {!AppConfig.isWeb && (
          <MenuItem
            data-tid="showInFileManager"
            onClick={showInFileManager}
          >
            <ListItemIcon>
              <OpenFolderNativelyIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:showInFileManager')} />
          </MenuItem>
        )}
        {!props.perspectiveMode && (
          <Divider />
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem
            data-tid="newSubDirectory"
            onClick={showCreateDirectoryDialog}
          >
            <ListItemIcon>
              <NewFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:newSubdirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem data-tid="createNewFile" onClick={createNewFile}>
            <ListItemIcon>
              <NewFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:newFileNote')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem data-tid="addExistingFile" onClick={addExistingFile}>
            <ListItemIcon>
              <AddExistingFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:showAddFileDialog')} />
          </MenuItem>
        )}
        {AppConfig.isCordova && (
          <MenuItem data-tid="takePicture" onClick={cameraTakePicture}>
            <ListItemIcon>
              <AddExistingFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:cameraTakePicture')} />
          </MenuItem>
        )}
        <Divider />
        { !props.perspectiveMode && (

            <MenuItem data-tid="openDefaultPerspective" onClick={() => switchPerspective('default')} title="Switch to default perspective">
              <ListItemIcon>
                <DefaultPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Default perspective" />
            </MenuItem>
            <MenuItem data-tid="openGalleryPerspective" onClick={() => switchPerspective('gallery')} title="Switch to gallery perspective">
              <ListItemIcon>
                <GalleryPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Gallery Perspective" />
            </MenuItem>
            <MenuItem data-tid="openMapiquePerspective" onClick={() => switchPerspective('mapique')} title="Switch to mapique perspective">
              <ListItemIcon>
                <MapiquePerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Mapique Perspective" />
            </MenuItem>
            {/* <MenuItem data-tid="openTreeVizPerspective" onClick={() => switchPerspective('treeviz')} title="Switch to tree visualization perspective">
              <ListItemIcon>
                <TreeVizPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="TreeViz Perspective" />
            </MenuItem> */}
            <Divider />

        )}
        {/* {!props.isReadOnlyMode && (
          <React.Fragment>
            <MenuItem data-tid="extractContent" onClick={initContentExtraction}>
              <ListItemIcon>
                <ContentExtractionIcon />
              </ListItemIcon>
              <ListItemText primary={i18n.t('core:startContentExtraction')} />
            </MenuItem>
          </React.Fragment>
        )} */}
        <MenuItem data-tid="showProperties" onClick={showProperties}>
          <ListItemIcon>
            <SettingsIcon />
          </ListItemIcon>
          <ListItemText primary={i18n.t('core:directoryPropertiesTitle')} />
        </MenuItem>
      </Menu>
      <input
        style={{ display: 'none' }}
        ref={input => {
          fileInput = input;
        }}
        accept="*"
        type="file"
        onChange={handleFileInputChange}
      />

  );
};

function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    showNotification: AppActions.showNotification,
    extractContent: IOActions.extractContent
  }, dispatch);
}

export default connect(null, mapDispatchToProps)(DirectoryMenu);
<commit_msg>marking new perspectives as beta<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React, { useState } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import uuidv1 from 'uuid';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Divider from '@material-ui/core/Divider';
// import ContentExtractionIcon from '@material-ui/icons/TrackChanges';
import OpenFolderIcon from '@material-ui/icons/SubdirectoryArrowLeft';
import AddExistingFileIcon from '@material-ui/icons/ExitToApp';
import OpenFolderNativelyIcon from '@material-ui/icons/Launch';
import AutoRenew from '@material-ui/icons/Autorenew';
import DefaultPerspectiveIcon from '@material-ui/icons/GridOn';
import GalleryPerspectiveIcon from '@material-ui/icons/Camera';
import MapiquePerspectiveIcon from '@material-ui/icons/Map';
import TreeVizPerspectiveIcon from '@material-ui/icons/AccountTree';
import NewFileIcon from '@material-ui/icons/InsertDriveFile';
import NewFolderIcon from '@material-ui/icons/CreateNewFolder';
import RenameFolderIcon from '@material-ui/icons/FormatTextdirectionLToR';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import SettingsIcon from '@material-ui/icons/Settings';
import ConfirmDialog from '../dialogs/ConfirmDialog';
import CreateDirectoryDialog from '../dialogs/CreateDirectoryDialog';
import RenameDirectoryDialog from '../dialogs/RenameDirectoryDialog';
import AppConfig from '../../config';
import i18n from '../../services/i18n';
import { extractFileName, normalizePath } from '../../utils/paths'; // extractFileExtension
import PlatformIO from '../../services/platform-io';
import { formatDateTime4Tag } from '../../utils/misc';
import { actions as AppActions } from '../../reducers/app';
import IOActions from '../../reducers/io-actions';

type Props = {
  open: boolean,
  onClose: () => void,
  anchorEl: Object | null,
  directoryPath: string,
  loadDirectoryContent: (path: string) => void,
  openDirectory: (path: string) => void,
  openFile: (path: string, isFile: boolean) => void,
  deleteDirectory: (path: string) => void,
  reflectCreateEntry: (path: string, isFile: boolean) => void,
  toggleCreateFileDialog: () => void,
  // extractContent: (config: Object) => void,
  switchPerspective: (perspectiveId: string) => void,
  perspectiveMode: boolean,
  showNotification: (
    text: string,
    notificationType: string,
    autohide: boolean
  ) => void,
  isReadOnlyMode: boolean
};

const DirectoryMenu = (props: Props) => {
  let fileInput; // Object | null;

  const [isCreateDirectoryDialogOpened, setIsCreateDirectoryDialogOpened] = useState(false);
  const [isDeleteDirectoryDialogOpened, setIsDeleteDirectoryDialogOpened] = useState(false);
  const [isRenameDirectoryDialogOpened, setIsRenameDirectoryDialogOpened] = useState(false);

  function reloadDirectory() {
    props.onClose();
    props.loadDirectoryContent(props.directoryPath);
  }

  function openDirectory() {
    props.onClose();
    props.loadDirectoryContent(props.directoryPath);
  }

  function showProperties() {
    props.onClose();
    props.openFile(props.directoryPath, false);
  }

  // function initContentExtraction() {
  //   props.onClose();
  //   props.extractContent({ EXIFGeo: true });
  // }

  function switchPerspective(perspectiveId) {
    props.onClose();
    props.switchPerspective(perspectiveId);
  }

  function showDeleteDirectoryDialog() {
    props.onClose();
    setIsDeleteDirectoryDialogOpened(true);
  }

  function showRenameDirectoryDialog() {
    props.onClose();
    setIsRenameDirectoryDialogOpened(true);
  }

  function showCreateDirectoryDialog() {
    props.onClose();
    setIsCreateDirectoryDialogOpened(true);
  }

  function createNewFile() {
    props.onClose();
    props.toggleCreateFileDialog();
  }

  function handleCloseDialogs() {
    setIsCreateDirectoryDialogOpened(false);
    setIsDeleteDirectoryDialogOpened(false);
    setIsRenameDirectoryDialogOpened(false);
  }

  function showInFileManager() {
    props.onClose();
    props.openDirectory(props.directoryPath);
  }

  function addExistingFile() {
    props.onClose();
    fileInput.click();
  }

  function onFail(message) {
    console.log('Camera Failed: ' + message);
  }

  function onCameraSuccess(imageURL) {
    window.resolveLocalFileSystemURL(imageURL, (fp) => {
      moveFile(fp.nativeURL);
    }, () => { console.log('Failed to get filesystem url'); });
  }

  function moveFile(filePath) {
    const fileName = 'IMG_TS' + AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + '.jpg';
    const newFilePath =
      normalizePath(props.directoryPath) +
      AppConfig.dirSeparator +
      fileName;

    PlatformIO.renameFilePromise(filePath, newFilePath)
      .then(() => {
        props.showNotification(
          'File ' + newFilePath + ' successfully imported.',
          'default',
          true
        );
        props.reflectCreateEntry(newFilePath, true);
        return true;
      })
      .catch(error => {
        // TODO showAlertDialog("Saving " + filePath + " failed.");
        console.error('Save to file ' + newFilePath + ' failed ' + error);
        props.showNotification(
          'Importing file ' + newFilePath + ' failed.',
          'error',
          true
        );
        return true;
      });
  }

  // function loadImageLocal() {
  //   props.onClose();
  //   navigator.camera.getPicture(onCameraSuccess, onFail, {
  //     destinationType: Camera.DestinationType.FILE_URI,
  //     sourceType: Camera.PictureSourceType.PHOTOLIBRARY
  //   });
  // }

  function cameraTakePicture() {
    props.onClose();
    navigator.camera.getPicture(onCameraSuccess, onFail, {
      // quality: 50,
      destinationType: Camera.DestinationType.FILE_URI, // DATA_URL, // Return base64 encoded string
      // encodingType: Camera.EncodingType.JPEG,
      mediaType: Camera.MediaType.PICTURE // ALLMEDIA
    });
  }

  function handleFileInputChange(selection: Object) {
    // console.log("Selected File: "+JSON.stringify(selection.currentTarget.files[0]));
    const file = selection.currentTarget.files[0];
    const filePath =
      normalizePath(props.directoryPath) +
      AppConfig.dirSeparator +
      decodeURIComponent(file.name);

    const reader = new FileReader();
    reader.onload = event => {
      // console.log('Content on file read complete: ' + JSON.stringify(event));
      // change name for ios fakepath
      // if (AppConfig.isCordovaiOS) {
      //   const fileExt = extractFileExtension(addFileInputName);
      //   addFileInputName = AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + fileExt;
      // }
      // TODO event.currentTarget.result is ArrayBuffer
      // Sample call from PRO version using content = Utils.base64ToArrayBuffer(baseString);
      PlatformIO.getPropertiesPromise(filePath).then((entryProps) => {
        if (entryProps) {
          props.showNotification(
            'File with the same name already exist, importing skipped!',
            'warning',
            true
          );
        } else {
          PlatformIO.saveBinaryFilePromise(
            filePath,
            event.currentTarget.result,
            true
          )
            .then(() => {
              props.showNotification(
                'File ' + filePath + ' successfully imported.',
                'default',
                true
              );
              props.reflectCreateEntry(filePath, true);
              return true;
            })
            .catch(error => {
              // TODO showAlertDialog("Saving " + filePath + " failed.");
              console.error('Save to file ' + filePath + ' failed ' + error);
              props.showNotification(
                'Importing file ' + filePath + ' failed.',
                'error',
                true
              );
              return true;
            });
        }
        return true;
      }).catch((err) => {
        console.log('Error getting properties ' + err);
      });
    };

    if (AppConfig.isCordova) {
      reader.readAsDataURL(file);
    } else {
      reader.readAsArrayBuffer(file);
    }
  }

  return (

      <RenameDirectoryDialog
        key={uuidv1()}
        open={isRenameDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        selectedDirectoryPath={props.directoryPath}
      />
      <CreateDirectoryDialog
        key={uuidv1()}
        open={isCreateDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        selectedDirectoryPath={props.directoryPath}
      />
      <ConfirmDialog
        open={isDeleteDirectoryDialogOpened}
        onClose={handleCloseDialogs}
        title={i18n.t('core:deleteDirectoryTitleConfirm')}
        content={i18n.t('core:deleteDirectoryContentConfirm', {
          dirPath: props.directoryPath
            ? extractFileName(props.directoryPath)
            : ''
        })}
        confirmCallback={result => {
          if (result) {
            props.deleteDirectory(props.directoryPath);
          }
        }}
        confirmDialogContent={'confirmDialogContent'}
        cancelDialogTID={'cancelDeleteDirectoryDialog'}
        confirmDialogTID={'confirmDeleteDirectoryDialog'}
      />
      <Menu
        anchorEl={props.anchorEl}
        open={props.open}
        onClose={props.onClose}
      >
        {props.perspectiveMode && (
          <MenuItem
            data-tid="openDirectory"
            onClick={openDirectory}
          >
            <ListItemIcon>
              <OpenFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:openDirectory')} />
          </MenuItem>
        )}
        {!props.perspectiveMode && (
          <MenuItem data-tid="reloadDirectory" onClick={reloadDirectory}>
            <ListItemIcon>
              <AutoRenew />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:reloadDirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && (
          <MenuItem
            data-tid="renameDirectory"
            onClick={showRenameDirectoryDialog}
          >
            <ListItemIcon>
              <RenameFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:renameDirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && (
          <MenuItem
            data-tid="deleteDirectory"
            onClick={showDeleteDirectoryDialog}
          >
            <ListItemIcon>
              <DeleteForeverIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:deleteDirectory')} />
          </MenuItem>
        )}
        {!AppConfig.isWeb && (
          <MenuItem
            data-tid="showInFileManager"
            onClick={showInFileManager}
          >
            <ListItemIcon>
              <OpenFolderNativelyIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:showInFileManager')} />
          </MenuItem>
        )}
        {!props.perspectiveMode && (
          <Divider />
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem
            data-tid="newSubDirectory"
            onClick={showCreateDirectoryDialog}
          >
            <ListItemIcon>
              <NewFolderIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:newSubdirectory')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem data-tid="createNewFile" onClick={createNewFile}>
            <ListItemIcon>
              <NewFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:newFileNote')} />
          </MenuItem>
        )}
        {!props.isReadOnlyMode && !props.perspectiveMode && (
          <MenuItem data-tid="addExistingFile" onClick={addExistingFile}>
            <ListItemIcon>
              <AddExistingFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:showAddFileDialog')} />
          </MenuItem>
        )}
        {AppConfig.isCordova && (
          <MenuItem data-tid="takePicture" onClick={cameraTakePicture}>
            <ListItemIcon>
              <AddExistingFileIcon />
            </ListItemIcon>
            <ListItemText primary={i18n.t('core:cameraTakePicture')} />
          </MenuItem>
        )}
        <Divider />
        { !props.perspectiveMode && (

            <MenuItem data-tid="openDefaultPerspective" onClick={() => switchPerspective('default')} title="Switch to default perspective">
              <ListItemIcon>
                <DefaultPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Default perspective" />
            </MenuItem>
            <MenuItem data-tid="openGalleryPerspective" onClick={() => switchPerspective('gallery')} title="Switch to gallery perspective">
              <ListItemIcon>
                <GalleryPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Gallery Perspective - Beta" />
            </MenuItem>
            <MenuItem data-tid="openMapiquePerspective" onClick={() => switchPerspective('mapique')} title="Switch to mapique perspective">
              <ListItemIcon>
                <MapiquePerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="Mapique Perspective - Beta" />
            </MenuItem>
            {/* <MenuItem data-tid="openTreeVizPerspective" onClick={() => switchPerspective('treeviz')} title="Switch to tree visualization perspective">
              <ListItemIcon>
                <TreeVizPerspectiveIcon />
              </ListItemIcon>
              <ListItemText primary="TreeViz Perspective" />
            </MenuItem> */}
            <Divider />

        )}
        {/* {!props.isReadOnlyMode && (
          <React.Fragment>
            <MenuItem data-tid="extractContent" onClick={initContentExtraction}>
              <ListItemIcon>
                <ContentExtractionIcon />
              </ListItemIcon>
              <ListItemText primary={i18n.t('core:startContentExtraction')} />
            </MenuItem>
          </React.Fragment>
        )} */}
        <MenuItem data-tid="showProperties" onClick={showProperties}>
          <ListItemIcon>
            <SettingsIcon />
          </ListItemIcon>
          <ListItemText primary={i18n.t('core:directoryPropertiesTitle')} />
        </MenuItem>
      </Menu>
      <input
        style={{ display: 'none' }}
        ref={input => {
          fileInput = input;
        }}
        accept="*"
        type="file"
        onChange={handleFileInputChange}
      />

  );
};

function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    showNotification: AppActions.showNotification,
    extractContent: IOActions.extractContent
  }, dispatch);
}

export default connect(null, mapDispatchToProps)(DirectoryMenu);
<|endoftext|>