<commit_before>/*
 ******************************************************************************
 Copyright (c) 2016 Particle Industries, Inc.  All rights reserved.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this program; if not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************
 */

import {LibraryNotFoundError, LibraryRepositoryError} from '../src/librepo';
import {LibraryRepository, Library, LibraryFile} from '../src/librepo';

const chai = require('chai');
const sinon = require('sinon');
chai.use(require('sinon-chai'));
chai.use(require('chai-as-promised'));
const expect = chai.expect;

function getClassName(inst) {
	return Object.prototype.toString.call(inst).match(/^\[object\s(.*)\]$/)[1];
}

describe('LibraryManager', () => {

	describe('Library Errors', () => {

		describe('LibraryRepositoryError', () => {
			it('has repo property', () => {
				let repo = {};
				let sut = new LibraryRepositoryError(repo);
				expect(sut.repo).to.equal(repo);
			});

			it("can use instanceof", () => {
				const sut = new LibraryRepositoryError();
				expect(sut).to.be.an.instanceof(LibraryRepositoryError);
				expect(sut instanceof LibraryRepositoryError).to.be.true;
			});
		});

		describe('LibraryNotFoundError', () => {
			it('has repo and library properties', () => {
				let repo = {};
				let library = 'uberlib';
				let sut = new LibraryNotFoundError(repo, library);
				expect(sut.library).to.equal(library);
				expect(sut.repo).to.equal(repo);
			});

			it('has a message', () => {
				let sut = new LibraryNotFoundError('uberrepo', 'uberlib');
				expect(sut.toString()).to.equal('LibraryNotFoundError: library \'uberlib\' not found in repo \'uberrepo\'.');
			});
		});
	});

	describe('Library', () => {

		it('has a name', () => {
			let sut = new Library('Borgian');
			return expect(sut.name).to.equal('Borgian');
		});

		it('has no files', () => {
			let sut = new Library();
			return expect(sut.files()).to.eventually.have.length(0);
		});
	});

	describe('LibraryFile', () => {
		it('has a name and a type', () => {
			let name = {};
			let type = {};
			let sut = new LibraryFile(name, type);
			expect(sut.name).to.equal(name);
			expect(sut.type).to.equal(type);
		});

		it('has streamable content', () => {
			let sut = new LibraryFile('name', 'type');
			let stream = {end: sinon.spy()};
			let p = sut.content(stream);
			expect(stream.end).to.be.calledOnce;
			return expect(p).to.eventually.equal(stream);
		});
	});

	describe('LibraryRepository', () => {
		it('raises an error when fetching a library by name', () => {
			let sut = new LibraryRepository();
			return expect(sut.fetch('uberlib')).eventually.rejected.deep.equal(new LibraryNotFoundError(sut, 'uberlib'));
		});

		it('has no libraries', () => {
			let sut = new LibraryRepository();
			return expect(sut.names()).eventually.to.have.length(0);
		});
	});

});
<commit_msg>workaround for instanceof tests<commit_after>/*
 ******************************************************************************
 Copyright (c) 2016 Particle Industries, Inc.  All rights reserved.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this program; if not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************
 */

import {LibraryNotFoundError, LibraryRepositoryError} from '../src/librepo';
import {LibraryRepository, Library, LibraryFile} from '../src/librepo';

const chai = require('chai');
const sinon = require('sinon');
chai.use(require('sinon-chai'));
chai.use(require('chai-as-promised'));
const expect = chai.expect;

describe('LibraryManager', () => {

	describe('Library Errors', () => {

		describe('LibraryRepositoryError', () => {
			it('has repo property', () => {
				let repo = {};
				let sut = new LibraryRepositoryError(repo);
				expect(sut.repo).to.equal(repo);
			});

			it('can use instanceof', () => {
				const sut = new LibraryRepositoryError();
				// using instanceof doesn't work
				// expect(sut).to.be.an.instanceof(LibraryRepositoryError);
				// expect(sut instanceof LibraryRepositoryError).to.be.true;
				expect(sut.name).to.be.string('LibraryRepositoryError');
			});
		});

		describe('LibraryNotFoundError', () => {
			it('has repo and library properties', () => {
				let repo = {};
				let library = 'uberlib';
				let sut = new LibraryNotFoundError(repo, library);
				expect(sut.library).to.equal(library);
				expect(sut.repo).to.equal(repo);
			});

			it('has a message', () => {
				let sut = new LibraryNotFoundError('uberrepo', 'uberlib');
				expect(sut.toString()).to.equal('LibraryNotFoundError: library \'uberlib\' not found in repo \'uberrepo\'.');
			});
		});
	});

	describe('Library', () => {

		it('has a name', () => {
			let sut = new Library('Borgian');
			return expect(sut.name).to.equal('Borgian');
		});

		it('has no files', () => {
			let sut = new Library();
			return expect(sut.files()).to.eventually.have.length(0);
		});
	});

	describe('LibraryFile', () => {
		it('has a name and a type', () => {
			let name = {};
			let type = {};
			let sut = new LibraryFile(name, type);
			expect(sut.name).to.equal(name);
			expect(sut.type).to.equal(type);
		});

		it('has streamable content', () => {
			let sut = new LibraryFile('name', 'type');
			let stream = {end: sinon.spy()};
			let p = sut.content(stream);
			expect(stream.end).to.be.calledOnce;
			return expect(p).to.eventually.equal(stream);
		});
	});

	describe('LibraryRepository', () => {
		it('raises an error when fetching a library by name', () => {
			let sut = new LibraryRepository();
			return expect(sut.fetch('uberlib')).eventually.rejected.deep.equal(new LibraryNotFoundError(sut, 'uberlib'));
		});

		it('has no libraries', () => {
			let sut = new LibraryRepository();
			return expect(sut.names()).eventually.to.have.length(0);
		});
	});

});
<|endoftext|>


<commit_before>function currentQueryString(){
	return returnQueryString(window.location.href);
}

function returnQueryString(url){
	return url.split("?")[1];
}

function removeQueryString(url){
  return url.split("?")[0];
}

function stripQueryString(url){
	return removeQueryString(url);
}

function toTitleCase(str){
    return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();

   return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    // return elemTop >= docViewTop;
}

function isFullyInView(e){
	isScrolledIntoView(e);
}

function isInView(e, modifier){
	// modifier is integer number of pixels the element can be scrolled into view, before return true
	if(typeof modifier == 'undefined'){
		modifier = 0;
	}
    var $e = $(e);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $e.offset().top;
    // var elemBottom = elemTop + $elem.height();
   return elemTop + modifier < docViewBottom;
}

function doMethods(methods){
  //accepts either a method or an array of methods. Put method in array if necessary
  if(typeof methods == "function"){methods = [methods];}

  methods.forEach(function(method){
    method();
  });
}

//-----rgb to hex
var hexDigits = new Array("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");

function rgb2hex(rgb) {
  rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
  return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}

function hex(x) {
  return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
}

function getCookie(cname) {
	var name = cname + "=";
	var ca = document.cookie.split(';');
	for(var i=0; i<ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1);
		if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
	}
	return "";
}

  function putInColsV(selector, item_selector){
    // Cols are designated with .anchor_col
    // Headers are desinated with .header_row
    var results = $(selector);

    for(var i = 0; i < results.length; i++){
      var r = $(results[i]);
      var rows = r.find(item_selector);
      var cols = r.find('.anchor_col');

      var per_col = parseInt(rows.length / cols.length);
      if(rows.length % cols.length != 0) per_col++;

      var first_index = 0;
      for(var j = 0; j < cols.length; j++){
        var col = $(cols[j]);
        var some_rows = rows.slice(first_index, first_index + per_col);
        if(some_rows.length > 0){
          col.append(some_rows);
        } else {
          col.find('.header_row').hide();
        }
        first_index = first_index + per_col;
      }
    }
  }

	function imageExists(url){
	var http = new XMLHttpRequest();
	http.open('HEAD', url, false);
	http.send();
	return http.status != 404;
}

function showOnlyAtIndex(klass, nth){
	var elements = document.getElementsByClassName(klass);
	if(nth < 0){
		nth = elements.length + nth;
	}

	// Could use some error checking for nth values that do not make sense

	for(var i = 0; i < elements.length; i++){
		elements[i].style.display = 'none';
	}
	elements[nth].style.display = "block";
}

function showOnlyFirst(klass){
	showOnlyAtIndex(klass, 1);
}

function showOnlyLast(klass){
	showOnlyAtIndex(klass, -1);
}
<commit_msg>Only At Index fix<commit_after>function currentQueryString(){
	return returnQueryString(window.location.href);
}

function returnQueryString(url){
	return url.split("?")[1];
}

function removeQueryString(url){
  return url.split("?")[0];
}

function stripQueryString(url){
	return removeQueryString(url);
}

function toTitleCase(str){
    return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

function isScrolledIntoView(elem){
    var $elem = $(elem);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $elem.offset().top;
    var elemBottom = elemTop + $elem.height();

   return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
    // return elemTop >= docViewTop;
}

function isFullyInView(e){
	isScrolledIntoView(e);
}

function isInView(e, modifier){
	// modifier is integer number of pixels the element can be scrolled into view, before return true
	if(typeof modifier == 'undefined'){
		modifier = 0;
	}
    var $e = $(e);
    var $window = $(window);

    var docViewTop = $window.scrollTop();
    var docViewBottom = docViewTop + $window.height();

    var elemTop = $e.offset().top;
    // var elemBottom = elemTop + $elem.height();
   return elemTop + modifier < docViewBottom;
}

function doMethods(methods){
  //accepts either a method or an array of methods. Put method in array if necessary
  if(typeof methods == "function"){methods = [methods];}

  methods.forEach(function(method){
    method();
  });
}

//-----rgb to hex
var hexDigits = new Array("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");

function rgb2hex(rgb) {
  rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
  return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}

function hex(x) {
  return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
}

function getCookie(cname) {
	var name = cname + "=";
	var ca = document.cookie.split(';');
	for(var i=0; i<ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1);
		if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
	}
	return "";
}

  function putInColsV(selector, item_selector){
    // Cols are designated with .anchor_col
    // Headers are desinated with .header_row
    var results = $(selector);

    for(var i = 0; i < results.length; i++){
      var r = $(results[i]);
      var rows = r.find(item_selector);
      var cols = r.find('.anchor_col');

      var per_col = parseInt(rows.length / cols.length);
      if(rows.length % cols.length != 0) per_col++;

      var first_index = 0;
      for(var j = 0; j < cols.length; j++){
        var col = $(cols[j]);
        var some_rows = rows.slice(first_index, first_index + per_col);
        if(some_rows.length > 0){
          col.append(some_rows);
        } else {
          col.find('.header_row').hide();
        }
        first_index = first_index + per_col;
      }
    }
  }

	function imageExists(url){
	var http = new XMLHttpRequest();
	http.open('HEAD', url, false);
	http.send();
	return http.status != 404;
}

function showOnlyAtIndex(klass, nth){
	var elements = document.getElementsByClassName(klass);
	if(nth < 0){
		nth = elements.length + nth;
	}

	// Could use some error checking for nth values that do not make sense

	for(var i = 0; i < elements.length; i++){
		elements[i].style.display = 'none';
	}
	elements[nth].style.display = "block";
}

function showOnlyFirst(klass){
	showOnlyAtIndex(klass, 0);
}

function showOnlyLast(klass){
	showOnlyAtIndex(klass, -1);
}
<|endoftext|>


<commit_before>'use strict';
var decimalSeparator = localizedNum[1];
function localizeNum(num) {
   return num.toString().replace(".", decimalSeparator);
}

var ajaxUploaderCompatible = (function() {
   // Test for FileReader
   if (!window.FileReader) {
      return false;
   }

   // Test for AJAX2
   var xhr = new XMLHttpRequest();
   if (!xhr.upload) {
      return false;
   }

   // Everything OK, this browser is compatible
   return true;
})();

function $id(id) {
   var element = document.getElementById(id);
   if (element) {
      return element;
   } else {
      throw "no element with id \"" + id + "\"";
   }
}

// Global variables
var dropBox = $id("form_container"); // where files are drop
var file; // file to upload
var xhr; // AJAX upload request
var ceHack; // Firefox contenteditable hack
var disableCeHack = false; // Avoid the hack if possible

function setPage(name) {
   $id("page_select").style.display = "none";
   $id("page_confirm").style.display = "none";

   if (name == "select") {
      $id("page_"+name).style.display = "block";
   } else {
      $id("subpage_confirm").style.display = "none";
      $id("subpage_progress").style.display = "none";
      $id("subpage_finished").style.display = "none";
      $id("subpage_error").style.display = "none";

      // Show subpage
      if (name == "confirm") {
         $id("subpage_confirm").style.display = "block";
      } else if (name == "progress") {
         $id("subpage_progress").style.display = "block";
      } else if (name == "finished") {
         $id("subpage_finished").style.display = "block";
      } else if (name == "error") {
         $id("subpage_error").style.display = "block";
      }

      // Show page
      $id("page_confirm").style.display = "block";
   }
}

function initAjaxUploader() {
   $id("form_container").className = "ajax";

   // Create a hidden file input
   var fileHack = document.createElement("input");
   fileHack.setAttribute("type", "file");
   fileHack.setAttribute("id", "file_hack");
   // Internet Explorer 10 needs a bit more of tweaking in order for show the
   // file dialog on a single click.
   if (window.navigator.appName == "Microsoft Internet Explorer") {
      fileHack.style.fontSize = "200px";
   }
   fileHack.addEventListener("change", handleFileSelect);
   $id("page_select").appendChild(fileHack);

   // Focus file selector so it can be opened with Return key
   setTimeout(function() {
      // But do it in the next turn, otherwise it does not work in Firefox
      fileHack.focus();
   }, 0);

   // Bind drag and drop events
   dropBox.addEventListener("dragover", function(e) {
      e.preventDefault();
      e.stopPropagation();
      dropBox.className = "ajax hover";
   }, false);
   dropBox.addEventListener("dragleave", function() {
      dropBox.className = "ajax";
   }, false);
   dropBox.addEventListener("drop", handleDrop, false);

   // Bind paste events
   initPasteHandler();

   // Bind buttons
   $id("upload_button").addEventListener("click", startUpload, false);
   $id("confirm_cancel").addEventListener("click", function() {
      setPage("select"); }, false);
   $id("cancel_upload").addEventListener("click", cancelUpload, false);
   $id("store_more_button").addEventListener("click", storeMore, false);
   $id("retry_button").addEventListener("click", function() {
      setPage("confirm"); }, false);
}

function handleFileSelect(e) {
   // Called when the user picks a file in the file dialog

   var files = e.target.files;
   if (files.length == 1) {
      confirmFile(files[0]);
   }
}

function handleDrop(e) {
   // Prevent the browser from taking the default action (leaving the page)
   e.stopPropagation();
   e.preventDefault();

   // Remove .hover class
   dropBox.className = "ajax";

   var files = e.dataTransfer.files;
   if (files.length == 1) {
      confirmFile(files[0]);
   }
}

function fileIsPicture(file) {
   var types = [
      "image/png",
      "image/jpeg",
      "image/gif"
   ];
   for (var i=0; i < types.length; i++) {
      if (file.type == types[i]) {
         return true;
      }
   }
   return false;
}

function humanSize(byteSize) {
   var KiB = 1024;
   var MiB = 1024 * 1024;
   var GiB = 1024 * 1024 * 1024;
   var prec = 1;

   if (byteSize < KiB) {
      return byteSize + " bytes";
   } else if (byteSize < MiB) {
      return (byteSize / KiB).toFixed(prec) + " KiB";
   } else if (byteSize < GiB) {
      return (byteSize / MiB).toFixed(prec) + " MiB";
   } else {
      return (byteSize / GiB).toFixed(prec) + " GiB";
   }
}

function confirmFile(new_file) {
   file = new_file; // Make global
   $id("file_name").textContent = file.name;
   $id("file_size").textContent = localizeNum(humanSize(file.size));

   var imagePane = $id("image_pane");
   imagePane.innerHTML = "";
   if (fileIsPicture(file)) {
      imagePane.style.visibility = "visible";
      $id("page_confirm").className = "";

      var reader = new FileReader();
      reader.onload = function() {
         var img = document.createElement("img");
         img.setAttribute("src", reader.result);
         imagePane.appendChild(img);
      };
      reader.readAsDataURL(file);
   } else {
      imagePane.style.visibility = "hidden";
      $id("page_confirm").className = "no_image";
   }

   setPage("confirm");
}

function setProgress(completed, total) {
   var ratio = completed / total;
   $id("percent").textContent = (ratio * 100).toFixed(0) + " %";
   $id("filled_area").style.width = Math.min(100, ratio * 100) + "%";
}

function uploadProgress(e) {
   setProgress(e.loaded, file.size);
}

function startUpload() {
   setProgress(0, 100);
   setPage("progress");

   var formData = new FormData();
   formData.append("expires_in", $id("ajax_expires_in").value);
   formData.append("file", file);
   // If this is not a real File object, but a Blob object with a name
   // property, append a file name override field, as the browser will send
   // filename=blob.
   if (!(file instanceof File)) {
      formData.append("file_name_override", file.name);
   }

   xhr = new XMLHttpRequest();

   xhr.upload.onprogress = uploadProgress;
   xhr.onload = uploadComplete;
   xhr.error = uploadFailed;

   xhr.open("POST", "/api/v1/", true);
   xhr.send(formData);
}

function uploadComplete(e) {
   if (this.status == 200) {
      var data = JSON.parse(this.response);
      var urlField = $id("lasagna_url_field");
      urlField.value = data.url;
      setPage("finished");

      urlField.setSelectionRange(0, urlField.value.length);
      urlField.select();
   } else {
      uploadFailed();
   }
}

function cancelUpload() {
   xhr.abort();
   setPage("confirm");
}

function uploadFailed(e) {
   setPage("error");
}

function storeMore() {
   setPage("select");
}

function initPasteHandler() {
   // For Chrome
   document.addEventListener("paste", pasteHandler, false);

   // The rest of this function is the contenteditable hack for Firefox

   // Create a focusable but mostly hidden contenteditable div
   ceHack = document.createElement("div");
   ceHack.setAttribute("contenteditable", "true");
   ceHack.setAttribute("tabindex", "-1");
   ceHack.style.opacity = "0";
   ceHack.style.position = "absolute";
   ceHack.style.left = "-10000px";
   ceHack.style.top = "-10000px";
   document.getElementsByTagName("body")[0].appendChild(ceHack);

   // Focus ceHack on <C-v>
   window.addEventListener("keydown", function(e) {
      if (!disableCeHack && (e.ctrlKey || e.metaKey) && e.keyCode == 86) {
         ceHack.focus();
      }
   });

   // Listen for changes in ceHack
   var observer = new MutationObserver(firefoxPasteHandler);
   observer.observe(ceHack, {childList: true});
}

function pad(number, positions) {
   var numStr = new String(number);
   for (var i = positions - numStr.length; i > 0; i--) {
      numStr = "0" + numStr;
   }
   return numStr;
}

function defaultName(type) {
   var date = new Date();
   var dateStr = date.getFullYear() + "-" +
                  pad(date.getMonth() + 1, 2) + "-" +
                  pad(date.getDate(), 2) + "-" +
                  pad(date.getHours(), 2) + "-" +
                  pad(date.getMinutes(), 2) + "-" +
                  pad(date.getSeconds(), 2);
   var extensions = {
      "image/png": "png",
      "image/jpeg": "jpeg",
      "image/gif": "gif",
      "text/plain": "txt"
   }

   return dateStr + ("." + extensions[type] || "");
}

function pasteHandler(e) {
   var items = e.clipboardData.items;
   if (!items) return; // items is undefined in Firefox :(
   var file = null;
   for (var i = 0; i < items.length; i++) {
      var item = items[i];
      if (item.kind == "file") {
         file = item.getAsFile();
         break;
      }
   }
   if (file) {
      disableCeHack = true;
      file.name = file.name || defaultName(file.type);
      confirmFile(file);
   }
}

// http://stackoverflow.com/a/14930686/1777162
function dataURItoBlob(dataURI) {
   var byteString,
       mimestring

   if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
       byteString = atob(dataURI.split(',')[1])
   } else {
       byteString = decodeURI(dataURI.split(',')[1])
   }

   mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]

   var content = new Array();
   for (var i = 0; i < byteString.length; i++) {
       content[i] = byteString.charCodeAt(i)
   }

   return new Blob([new Uint8Array(content)], {type: mimestring});
}

function firefoxPasteHandler() {
   if (!disableCeHack) {
      var images = ceHack.getElementsByTagName("img");
      if (images.length > 0) {
         var img = images[0];
         var src = img.getAttribute("src");
         var blob;
         if (src.match(/https?:/)) {
            alert(noBrowserImagesError);
         } else if (src.lastIndexOf("data:", 0) === 0) {
            blob = dataURItoBlob(src);

            // A File object is a Blob plus a file name, but I'm not aware of
            // any way to convert a Blob in a File object with name.

            // File constructor should be able to do this, but it raises a
            // security exception on Firefox.

            // Instead, I'll put a name property in the Blob object itself.
            // Later, startUpload will detect that it does not have a real File
            // object, read this property and add an override field for the
            // file name.
            blob.name = defaultName(blob.type);
            confirmFile(blob);
         }
      }
   }
   // Don't leave old elements in the ceHack
   ceHack.innerHTML = "";
}

if (ajaxUploaderCompatible) {
   initAjaxUploader();
}
<commit_msg>File is immutable in newer browsers<commit_after>'use strict';
var decimalSeparator = localizedNum[1];
function localizeNum(num) {
   return num.toString().replace(".", decimalSeparator);
}

var ajaxUploaderCompatible = (function() {
   // Test for FileReader
   if (!window.FileReader) {
      return false;
   }

   // Test for AJAX2
   var xhr = new XMLHttpRequest();
   if (!xhr.upload) {
      return false;
   }

   // Everything OK, this browser is compatible
   return true;
})();

function $id(id) {
   var element = document.getElementById(id);
   if (element) {
      return element;
   } else {
      throw "no element with id \"" + id + "\"";
   }
}

// Global variables
var dropBox = $id("form_container"); // where files are drop
var file; // file to upload
var xhr; // AJAX upload request
var ceHack; // Firefox contenteditable hack
var disableCeHack = false; // Avoid the hack if possible

function setPage(name) {
   $id("page_select").style.display = "none";
   $id("page_confirm").style.display = "none";

   if (name == "select") {
      $id("page_"+name).style.display = "block";
   } else {
      $id("subpage_confirm").style.display = "none";
      $id("subpage_progress").style.display = "none";
      $id("subpage_finished").style.display = "none";
      $id("subpage_error").style.display = "none";

      // Show subpage
      if (name == "confirm") {
         $id("subpage_confirm").style.display = "block";
      } else if (name == "progress") {
         $id("subpage_progress").style.display = "block";
      } else if (name == "finished") {
         $id("subpage_finished").style.display = "block";
      } else if (name == "error") {
         $id("subpage_error").style.display = "block";
      }

      // Show page
      $id("page_confirm").style.display = "block";
   }
}

function initAjaxUploader() {
   $id("form_container").className = "ajax";

   // Create a hidden file input
   var fileHack = document.createElement("input");
   fileHack.setAttribute("type", "file");
   fileHack.setAttribute("id", "file_hack");
   // Internet Explorer 10 needs a bit more of tweaking in order for show the
   // file dialog on a single click.
   if (window.navigator.appName == "Microsoft Internet Explorer") {
      fileHack.style.fontSize = "200px";
   }
   fileHack.addEventListener("change", handleFileSelect);
   $id("page_select").appendChild(fileHack);

   // Focus file selector so it can be opened with Return key
   setTimeout(function() {
      // But do it in the next turn, otherwise it does not work in Firefox
      fileHack.focus();
   }, 0);

   // Bind drag and drop events
   dropBox.addEventListener("dragover", function(e) {
      e.preventDefault();
      e.stopPropagation();
      dropBox.className = "ajax hover";
   }, false);
   dropBox.addEventListener("dragleave", function() {
      dropBox.className = "ajax";
   }, false);
   dropBox.addEventListener("drop", handleDrop, false);

   // Bind paste events
   initPasteHandler();

   // Bind buttons
   $id("upload_button").addEventListener("click", startUpload, false);
   $id("confirm_cancel").addEventListener("click", function() {
      setPage("select"); }, false);
   $id("cancel_upload").addEventListener("click", cancelUpload, false);
   $id("store_more_button").addEventListener("click", storeMore, false);
   $id("retry_button").addEventListener("click", function() {
      setPage("confirm"); }, false);
}

function handleFileSelect(e) {
   // Called when the user picks a file in the file dialog

   var files = e.target.files;
   if (files.length == 1) {
      confirmFile(files[0]);
   }
}

function handleDrop(e) {
   // Prevent the browser from taking the default action (leaving the page)
   e.stopPropagation();
   e.preventDefault();

   // Remove .hover class
   dropBox.className = "ajax";

   var files = e.dataTransfer.files;
   if (files.length == 1) {
      confirmFile(files[0]);
   }
}

function fileIsPicture(file) {
   var types = [
      "image/png",
      "image/jpeg",
      "image/gif"
   ];
   for (var i=0; i < types.length; i++) {
      if (file.type == types[i]) {
         return true;
      }
   }
   return false;
}

function humanSize(byteSize) {
   var KiB = 1024;
   var MiB = 1024 * 1024;
   var GiB = 1024 * 1024 * 1024;
   var prec = 1;

   if (byteSize < KiB) {
      return byteSize + " bytes";
   } else if (byteSize < MiB) {
      return (byteSize / KiB).toFixed(prec) + " KiB";
   } else if (byteSize < GiB) {
      return (byteSize / MiB).toFixed(prec) + " MiB";
   } else {
      return (byteSize / GiB).toFixed(prec) + " GiB";
   }
}

function confirmFile(new_file) {
   file = new_file; // Make global
   $id("file_name").textContent = file.name;
   $id("file_size").textContent = localizeNum(humanSize(file.size));

   var imagePane = $id("image_pane");
   imagePane.innerHTML = "";
   if (fileIsPicture(file)) {
      imagePane.style.visibility = "visible";
      $id("page_confirm").className = "";

      var reader = new FileReader();
      reader.onload = function() {
         var img = document.createElement("img");
         img.setAttribute("src", reader.result);
         imagePane.appendChild(img);
      };
      reader.readAsDataURL(file);
   } else {
      imagePane.style.visibility = "hidden";
      $id("page_confirm").className = "no_image";
   }

   setPage("confirm");
}

function setProgress(completed, total) {
   var ratio = completed / total;
   $id("percent").textContent = (ratio * 100).toFixed(0) + " %";
   $id("filled_area").style.width = Math.min(100, ratio * 100) + "%";
}

function uploadProgress(e) {
   setProgress(e.loaded, file.size);
}

function startUpload() {
   setProgress(0, 100);
   setPage("progress");

   var formData = new FormData();
   formData.append("expires_in", $id("ajax_expires_in").value);
   formData.append("file", file);
   // If this is not a real File object, but a Blob object with a name
   // property, append a file name override field, as the browser will send
   // filename=blob.
   if (!(file instanceof File)) {
      formData.append("file_name_override", file.name);
   }

   xhr = new XMLHttpRequest();

   xhr.upload.onprogress = uploadProgress;
   xhr.onload = uploadComplete;
   xhr.error = uploadFailed;

   xhr.open("POST", "/api/v1/", true);
   xhr.send(formData);
}

function uploadComplete(e) {
   if (this.status == 200) {
      var data = JSON.parse(this.response);
      var urlField = $id("lasagna_url_field");
      urlField.value = data.url;
      setPage("finished");

      urlField.setSelectionRange(0, urlField.value.length);
      urlField.select();
   } else {
      uploadFailed();
   }
}

function cancelUpload() {
   xhr.abort();
   setPage("confirm");
}

function uploadFailed(e) {
   setPage("error");
}

function storeMore() {
   setPage("select");
}

function initPasteHandler() {
   // For Chrome
   document.addEventListener("paste", pasteHandler, false);

   // The rest of this function is the contenteditable hack for Firefox

   // Create a focusable but mostly hidden contenteditable div
   ceHack = document.createElement("div");
   ceHack.setAttribute("contenteditable", "true");
   ceHack.setAttribute("tabindex", "-1");
   ceHack.style.opacity = "0";
   ceHack.style.position = "absolute";
   ceHack.style.left = "-10000px";
   ceHack.style.top = "-10000px";
   document.getElementsByTagName("body")[0].appendChild(ceHack);

   // Focus ceHack on <C-v>
   window.addEventListener("keydown", function(e) {
      if (!disableCeHack && (e.ctrlKey || e.metaKey) && e.keyCode == 86) {
         ceHack.focus();
      }
   });

   // Listen for changes in ceHack
   var observer = new MutationObserver(firefoxPasteHandler);
   observer.observe(ceHack, {childList: true});
}

function pad(number, positions) {
   var numStr = new String(number);
   for (var i = positions - numStr.length; i > 0; i--) {
      numStr = "0" + numStr;
   }
   return numStr;
}

function defaultName(type) {
   var date = new Date();
   var dateStr = date.getFullYear() + "-" +
                  pad(date.getMonth() + 1, 2) + "-" +
                  pad(date.getDate(), 2) + "-" +
                  pad(date.getHours(), 2) + "-" +
                  pad(date.getMinutes(), 2) + "-" +
                  pad(date.getSeconds(), 2);
   var extensions = {
      "image/png": "png",
      "image/jpeg": "jpeg",
      "image/gif": "gif",
      "text/plain": "txt"
   }

   return dateStr + ("." + extensions[type] || "");
}

function pasteHandler(e) {
   var items = e.clipboardData.items;
   if (!items) return; // items is undefined in Firefox :(
   var file = null;
   for (var i = 0; i < items.length; i++) {
      var item = items[i];
      if (item.kind == "file") {
         file = item.getAsFile();
         break;
      }
   }
   if (file) {
      disableCeHack = true;
      var hasFileName = file.name && file.name.indexOf("image.") != 0;
      file = new File(file, hasFileName ? file.name : defaultName(file.type), {
            type: file.type
      });
      confirmFile(file);
   }
}

// http://stackoverflow.com/a/14930686/1777162
function dataURItoBlob(dataURI) {
   var byteString,
       mimestring

   if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
       byteString = atob(dataURI.split(',')[1])
   } else {
       byteString = decodeURI(dataURI.split(',')[1])
   }

   mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]

   var content = new Array();
   for (var i = 0; i < byteString.length; i++) {
       content[i] = byteString.charCodeAt(i)
   }

   return new Blob([new Uint8Array(content)], {type: mimestring});
}

function firefoxPasteHandler() {
   if (!disableCeHack) {
      var images = ceHack.getElementsByTagName("img");
      if (images.length > 0) {
         var img = images[0];
         var src = img.getAttribute("src");
         var blob;
         if (src.match(/https?:/)) {
            alert(noBrowserImagesError);
         } else if (src.lastIndexOf("data:", 0) === 0) {
            blob = dataURItoBlob(src);

            // A File object is a Blob plus a file name, but I'm not aware of
            // any way to convert a Blob in a File object with name.

            // File constructor should be able to do this, but it raises a
            // security exception on Firefox.

            // Instead, I'll put a name property in the Blob object itself.
            // Later, startUpload will detect that it does not have a real File
            // object, read this property and add an override field for the
            // file name.
            blob.name = defaultName(blob.type);
            confirmFile(blob);
         }
      }
   }
   // Don't leave old elements in the ceHack
   ceHack.innerHTML = "";
}

if (ajaxUploaderCompatible) {
   initAjaxUploader();
}
<|endoftext|>


<commit_before>function GameManager(size, InputManager, Actuator, StorageManager) {
  this.size           = size; // Size of the grid
  this.inputManager   = new InputManager;
  this.storageManager = new StorageManager;
  this.actuator       = new Actuator;

  this.startTiles     = 2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.storageManager.clearGameState();
  this.actuator.continueGame(); // Clear the game won/lost message
  this.setup();
};

// Keep playing after winning (allows going over 2048)
GameManager.prototype.keepPlaying = function () {
  if (confirm('You sure?')) {
    if (confirm('Like, really?')) {
      this.keepPlaying = true;
      this.actuator.continueGame(); // Clear the game won/lost message
    }
  }
};

// Return true if the game is lost, or has won and the user hasn't kept playing
GameManager.prototype.isGameTerminated = function () {
  return this.over || (this.won && !this.keepPlaying);
};

// Set up the game
GameManager.prototype.setup = function () {
  var previousState = this.storageManager.getGameState();

  // Reload the game from a previous game if present
  if (previousState) {
    this.grid        = new Grid(previousState.grid.size,
                                previousState.grid.cells); // Reload grid
    this.score       = previousState.score;
    this.over        = previousState.over;
    this.won         = previousState.won;
    this.keepPlaying = previousState.keepPlaying;
  } else {
    this.grid        = new Grid(this.size);
    this.score       = 0;
    this.over        = false;
    this.won         = false;
    this.keepPlaying = false;

    // Add the initial tiles
    this.addStartTiles();
  }

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? 2 : 4;
    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.storageManager.getBestScore() < this.score) {
    this.storageManager.setBestScore(this.score);
  }

  // Clear the state when the game is over (game over only, not win)
  if (this.over) {
    this.storageManager.clearGameState();
  } else {
    this.storageManager.setGameState(this.serialize());
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.storageManager.getBestScore(),
    terminated: this.isGameTerminated()
  });

};

// Represent the current game as an object
GameManager.prototype.serialize = function () {
  return {
    grid:        this.grid.serialize(),
    score:       this.score,
    over:        this.over,
    won:         this.won,
    keepPlaying: this.keepPlaying
  };
};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2: down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          var merged = new Tile(positions.next, tile.value * 2);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 4096) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // Up
    1: { x: 1,  y: 0 },  // Right
    2: { x: 0,  y: 1 },  // Down
    3: { x: -1, y: 0 }   // Left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<commit_msg>Hack the initial game grid to have a better starting setup<commit_after>function GameManager(size, InputManager, Actuator, StorageManager) {
  this.size           = size; // Size of the grid
  this.inputManager   = new InputManager;
  this.storageManager = new StorageManager;
  this.actuator       = new Actuator;

  this.startTiles     = 1; //2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.storageManager.clearGameState();
  this.actuator.continueGame(); // Clear the game won/lost message
  this.setup();
};

// Keep playing after winning (allows going over 2048)
GameManager.prototype.keepPlaying = function () {
  this.keepPlaying = true;
  this.actuator.continueGame(); // Clear the game won/lost message
};

// Return true if the game is lost, or has won and the user hasn't kept playing
GameManager.prototype.isGameTerminated = function () {
  return this.over || (this.won && !this.keepPlaying);
};

// Set up the game
GameManager.prototype.setup = function () {
  var previousState = this.storageManager.getGameState();

  // Reload the game from a previous game if present
  if (previousState) {
    this.grid        = new Grid(previousState.grid.size,
                                previousState.grid.cells); // Reload grid
    this.score       = previousState.score;
    this.over        = previousState.over;
    this.won         = previousState.won;
    this.keepPlaying = previousState.keepPlaying;
  } else {
    this.grid        = new Grid(this.size);
    this.score       = 0;
    this.over        = false;
    this.won         = false;
    this.keepPlaying = false;

    // Add the initial tiles
    this.addStartTiles();
  }

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  // Start the game out with a reasonable pre-play setup
  // (Getting to this pre-play setup is kinda boring...so I'm jumping to the
  //  chase with this hack)
  var y = 0;
  [128, 256, 512, 1024].forEach(function(value) {
    var tile = new Tile({x: 0, y: y}, value);
    this.grid.insertTile(tile);
    y++;
  }, this);

  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? 2 : 4;
    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.storageManager.getBestScore() < this.score) {
    this.storageManager.setBestScore(this.score);
  }

  // Clear the state when the game is over (game over only, not win)
  if (this.over) {
    this.storageManager.clearGameState();
  } else {
    this.storageManager.setGameState(this.serialize());
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.storageManager.getBestScore(),
    terminated: this.isGameTerminated()
  });

};

// Represent the current game as an object
GameManager.prototype.serialize = function () {
  return {
    grid:        this.grid.serialize(),
    score:       this.score,
    over:        this.over,
    won:         this.won,
    keepPlaying: this.keepPlaying
  };
};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2: down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          var merged = new Tile(positions.next, tile.value * 2);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 4096) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // Up
    1: { x: 1,  y: 0 },  // Right
    2: { x: 0,  y: 1 },  // Down
    3: { x: -1, y: 0 }   // Left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<|endoftext|>


<commit_before>/*
 * L.DomUtil contains various utility functions for working with DOM
 */

L.DomUtil = {
	get: function (id) {
		return (typeof id === 'string' ? document.getElementById(id) : id);
	},

	getStyle: function (el, style) {
		var value = el.style[style];
		if (!value && el.currentStyle) {
			value = el.currentStyle[style];
		}
		if (!value || value === 'auto') {
			var css = document.defaultView.getComputedStyle(el, null);
			value = css ? css[style] : null;
		}
		return (value === 'auto' ? null : value);
	},

	getViewportOffset: function (element) {
		var top = 0,
			left = 0,
			el = element,
			docBody = document.body;

		do {
			top += el.offsetTop || 0;
			left += el.offsetLeft || 0;

			if (el.offsetParent === docBody &&
					L.DomUtil.getStyle(el, 'position') === 'absolute') {
				break;
			}
			if (L.DomUtil.getStyle(el, 'position') === 'fixed') {
				top += docBody.scrollTop || 0;
				left += docBody.scrollLeft || 0;
				break;
			}

			el = el.offsetParent;
		} while (el);

		el = element;

		do {
			if (el === docBody) {
				break;
			}

			top -= el.scrollTop || 0;
			left -= el.scrollLeft || 0;

			el = el.parentNode;
		} while (el);

		return new L.Point(left, top);
	},

	create: function (tagName, className, container) {
		var el = document.createElement(tagName);
		el.className = className;
		if (container) {
			container.appendChild(el);
		}
		return el;
	},

	disableTextSelection: function () {
		if (document.selection && document.selection.empty) {
			document.selection.empty();
		}
		if (!this._onselectstart) {
			this._onselectstart = document.onselectstart;
			document.onselectstart = L.Util.falseFn;
		}
	},

	enableTextSelection: function () {
		document.onselectstart = this._onselectstart;
		this._onselectstart = null;
	},

	hasClass: function (el, name) {
		return (el.className.length > 0) &&
				new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className);
	},

	addClass: function (el, name) {
		if (!L.DomUtil.hasClass(el, name)) {
			el.className += (el.className ? ' ' : '') + name;
		}
	},

	removeClass: function (el, name) {
		el.className = el.className.replace(/(\S+)\s*/g, function (w, match) {
			if (match === name) {
				return '';
			}
			return w;
		}).replace(/^\s+/, '');
	},

	setOpacity: function (el, value) {
		if (L.Browser.ie) {
		    el.style.filter += value !== 1 ? 'alpha(opacity=' + Math.round(value * 100) + ')' : '';
		} else {
			el.style.opacity = value;
		}
	},

	//TODO refactor away this ugly translate/position mess

	testProp: function (props) {
		var style = document.documentElement.style;

		for (var i = 0; i < props.length; i++) {
			if (props[i] in style) {
				return props[i];
			}
		}
		return false;
	},

	getTranslateString: function (point) {
		//On webkit browsers (Chrome/Safari/MobileSafari/Android) using translate3d instead of translate
		// makes animation smoother as it ensures HW accel is used.
		//Firefox 13 doesn't care (same speed either way), Opera 12 doesn't support translate3d
		var is3d = L.Browser.webkit3d,
			open = 'translate' + (is3d ? '3d' : '') + '(',
			close = (is3d ? ',0' : '') + ')';

		return open + point.x + 'px,' + point.y + 'px' + close;
	},

	getScaleString: function (scale, origin) {
		var preTranslateStr = L.DomUtil.getTranslateString(origin),
			scaleStr = ' scale(' + scale + ') ',
			postTranslateStr = L.DomUtil.getTranslateString(origin.multiplyBy(-1));

		return preTranslateStr + scaleStr + postTranslateStr;
	},

	setPosition: function (el, point, disable3D) {
		el._leaflet_pos = point;
		if (!disable3D && L.Browser.any3d) {
			el.style[L.DomUtil.TRANSFORM] =  L.DomUtil.getTranslateString(point);

			if (L.Browser.mobileWebkit3d) {
				el.style.WebkitBackfaceVisibility = 'hidden';
			}
		} else {
			el.style.left = point.x + 'px';
			el.style.top = point.y + 'px';
		}
	},

	getPosition: function (el) {
		return el._leaflet_pos;
	}
};

L.Util.extend(L.DomUtil, {
	TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']),
	TRANSFORM: L.DomUtil.testProp(['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform'])
});
<commit_msg>Comment on why a work around is there<commit_after>/*
 * L.DomUtil contains various utility functions for working with DOM
 */

L.DomUtil = {
	get: function (id) {
		return (typeof id === 'string' ? document.getElementById(id) : id);
	},

	getStyle: function (el, style) {
		var value = el.style[style];
		if (!value && el.currentStyle) {
			value = el.currentStyle[style];
		}
		if (!value || value === 'auto') {
			var css = document.defaultView.getComputedStyle(el, null);
			value = css ? css[style] : null;
		}
		return (value === 'auto' ? null : value);
	},

	getViewportOffset: function (element) {
		var top = 0,
			left = 0,
			el = element,
			docBody = document.body;

		do {
			top += el.offsetTop || 0;
			left += el.offsetLeft || 0;

			if (el.offsetParent === docBody &&
					L.DomUtil.getStyle(el, 'position') === 'absolute') {
				break;
			}
			if (L.DomUtil.getStyle(el, 'position') === 'fixed') {
				top += docBody.scrollTop || 0;
				left += docBody.scrollLeft || 0;
				break;
			}

			el = el.offsetParent;
		} while (el);

		el = element;

		do {
			if (el === docBody) {
				break;
			}

			top -= el.scrollTop || 0;
			left -= el.scrollLeft || 0;

			el = el.parentNode;
		} while (el);

		return new L.Point(left, top);
	},

	create: function (tagName, className, container) {
		var el = document.createElement(tagName);
		el.className = className;
		if (container) {
			container.appendChild(el);
		}
		return el;
	},

	disableTextSelection: function () {
		if (document.selection && document.selection.empty) {
			document.selection.empty();
		}
		if (!this._onselectstart) {
			this._onselectstart = document.onselectstart;
			document.onselectstart = L.Util.falseFn;
		}
	},

	enableTextSelection: function () {
		document.onselectstart = this._onselectstart;
		this._onselectstart = null;
	},

	hasClass: function (el, name) {
		return (el.className.length > 0) &&
				new RegExp("(^|\\s)" + name + "(\\s|$)").test(el.className);
	},

	addClass: function (el, name) {
		if (!L.DomUtil.hasClass(el, name)) {
			el.className += (el.className ? ' ' : '') + name;
		}
	},

	removeClass: function (el, name) {
		el.className = el.className.replace(/(\S+)\s*/g, function (w, match) {
			if (match === name) {
				return '';
			}
			return w;
		}).replace(/^\s+/, '');
	},

	setOpacity: function (el, value) {
		if (L.Browser.ie) {
		    el.style.filter += value !== 1 ? 'alpha(opacity=' + Math.round(value * 100) + ')' : '';
		} else {
			el.style.opacity = value;
		}
	},

	//TODO refactor away this ugly translate/position mess

	testProp: function (props) {
		var style = document.documentElement.style;

		for (var i = 0; i < props.length; i++) {
			if (props[i] in style) {
				return props[i];
			}
		}
		return false;
	},

	getTranslateString: function (point) {
		//On webkit browsers (Chrome/Safari/MobileSafari/Android) using translate3d instead of translate
		// makes animation smoother as it ensures HW accel is used.
		//Firefox 13 doesn't care (same speed either way), Opera 12 doesn't support translate3d
		var is3d = L.Browser.webkit3d,
			open = 'translate' + (is3d ? '3d' : '') + '(',
			close = (is3d ? ',0' : '') + ')';

		return open + point.x + 'px,' + point.y + 'px' + close;
	},

	getScaleString: function (scale, origin) {
		var preTranslateStr = L.DomUtil.getTranslateString(origin),
			scaleStr = ' scale(' + scale + ') ',
			postTranslateStr = L.DomUtil.getTranslateString(origin.multiplyBy(-1));

		return preTranslateStr + scaleStr + postTranslateStr;
	},

	setPosition: function (el, point, disable3D) {
		el._leaflet_pos = point;
		if (!disable3D && L.Browser.any3d) {
			el.style[L.DomUtil.TRANSFORM] =  L.DomUtil.getTranslateString(point);

			//Work around for android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69)
			if (L.Browser.mobileWebkit3d) {
				el.style.WebkitBackfaceVisibility = 'hidden';
			}
		} else {
			el.style.left = point.x + 'px';
			el.style.top = point.y + 'px';
		}
	},

	getPosition: function (el) {
		return el._leaflet_pos;
	}
};

L.Util.extend(L.DomUtil, {
	TRANSITION: L.DomUtil.testProp(['transition', 'webkitTransition', 'OTransition', 'MozTransition', 'msTransition']),
	TRANSFORM: L.DomUtil.testProp(['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform'])
});
<|endoftext|>


<commit_before>'use strict';

angular.module('ddsApp').controller('FoyerRessourceTypesCtrl', function($scope, $stateParams, ressourceCategories, ressourceTypes, $state) {
    $scope.ressourceCategories = ressourceCategories;
    $scope.individuVM = $scope.individusVM[$stateParams.individu];
    var filteredRessourceTypes = _.filter(ressourceTypes, function(ressourceType) {
        return !_.contains(['pensionsAlimentairesVersees'], ressourceType.id);
    });
    $scope.ressourceTypesByCategories = _.groupBy(filteredRessourceTypes, 'category');

    var pageTitle = function() {
        switch ($scope.individuVM.individu.role) {
            case 'demandeur':
                return 'Vos ressources';
            case 'conjoint':
                return 'Les ressources de votre conjoint';
            default:
                return 'Les ressources de ' + $scope.individuVM.individu.firstName;
        }
    };
    $scope.pageTitle = pageTitle();

    var applySelectedRessources = function() {
        var currentRessources = $scope.individuVM.ressources;
        $scope.individuVM.ressources = [];
        ressourceTypes.forEach(function(ressourceType) {
            if (!$scope.individuVM.selectedRessourceTypes[ressourceType.id]) {
                return;
            }
            var ressource = _.find(currentRessources, { type: ressourceType });
            if (!ressource) {
                ressource = {
                    type: ressourceType,
                    montantAnnuel: 0
                };

                if (ressourceType.category == 'tns') {
                    switch (ressourceType.id) {
                        case 'caMicroEntreprise':
                            ressource.tnsActiviteType = 'bic';
                            break;
                        case 'caAutoEntrepreneur':
                            ressource.tnsActiviteType = 'bic';
                            ressource.montantsMensuels = [0, 0, 0];
                            ressource.onGoing = true;
                            break;
                        case 'autresRevenusTns':
                            ressource.tnsActiviteType = 'bic';
                            ressource.caAnnuel = 0;
                            break;
                    }
                } else {
                    ressource.montantsMensuels = [0, 0, 0];
                    ressource.onGoing = true;
                }
            }
            $scope.individuVM.ressources.push(ressource);
        });
    };

    $scope.submit = function() {
        applySelectedRessources();
        $state.go('foyer.ressources');
    };
});
<commit_msg>Simplification applySelectedRessources<commit_after>'use strict';

angular.module('ddsApp').controller('FoyerRessourceTypesCtrl', function($scope, $stateParams, ressourceCategories, ressourceTypes, $state) {
    $scope.ressourceCategories = ressourceCategories;
    $scope.individuVM = $scope.individusVM[$stateParams.individu];
    var filteredRessourceTypes = _.filter(ressourceTypes, function(ressourceType) {
        return !_.contains(['pensionsAlimentairesVersees'], ressourceType.id);
    });
    $scope.ressourceTypesByCategories = _.groupBy(filteredRessourceTypes, 'category');

    var pageTitle = function() {
        switch ($scope.individuVM.individu.role) {
            case 'demandeur':
                return 'Vos ressources';
            case 'conjoint':
                return 'Les ressources de votre conjoint';
            default:
                return 'Les ressources de ' + $scope.individuVM.individu.firstName;
        }
    };

    var DEFAULT_RESOURCE = {
        montantAnnuel: 0,
        caAnnuel: 0,
        tnsActiviteType: 'bic',
        montantsMensuels: [0, 0, 0],
        onGoing: true,
    };

    $scope.pageTitle = pageTitle();

    var applySelectedRessources = function() {
        var currentRessources = $scope.individuVM.ressources;
        $scope.individuVM.ressources = [];
        ressourceTypes.forEach(function(ressourceType) {
            if (!$scope.individuVM.selectedRessourceTypes[ressourceType.id]) {
                return;
            }
            var ressource = _.find(currentRessources, { type: ressourceType });
            if (!ressource) {
                ressource = {
                    type: ressourceType,
                };
                _.each(DEFAULT_RESOURCE, function(value, key){
                    ressource[key] = value;
                });
            }
            $scope.individuVM.ressources.push(ressource);
        });
    };

    $scope.submit = function() {
        applySelectedRessources();
        $state.go('foyer.ressources');
    };
});
<|endoftext|>


<commit_before>define(["sugar-web/env", "webL10n"], function (env, webL10n) {

  var connectedPeople = {};
  var xoLogo = '<?xml version="1.0" ?><!DOCTYPE svg  PUBLIC \'-//W3C//DTD SVG 1.1//EN\'  \'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\' [<!ENTITY stroke_color "#010101"><!ENTITY fill_color "#FFFFFF">]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="stock-xo_1_"><path d="M33.233,35.1l10.102,10.1c0.752,0.75,1.217,1.783,1.217,2.932   c0,2.287-1.855,4.143-4.146,4.143c-1.145,0-2.178-0.463-2.932-1.211L27.372,40.961l-10.1,10.1c-0.75,0.75-1.787,1.211-2.934,1.211   c-2.284,0-4.143-1.854-4.143-4.141c0-1.146,0.465-2.184,1.212-2.934l10.104-10.102L11.409,24.995   c-0.747-0.748-1.212-1.785-1.212-2.93c0-2.289,1.854-4.146,4.146-4.146c1.143,0,2.18,0.465,2.93,1.214l10.099,10.102l10.102-10.103   c0.754-0.749,1.787-1.214,2.934-1.214c2.289,0,4.146,1.856,4.146,4.145c0,1.146-0.467,2.18-1.217,2.932L33.233,35.1z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/><circle cx="27.371" cy="10.849" fill="&fill_color;" r="8.122" stroke="&stroke_color;" stroke-width="3.5"/></g></svg>';

  var language;
  window.addEventListener('localized', function(e) {
      if (e.language != language) {
          setTimeout(function() {
            webL10n.language.code = language;
          }, 50);
      }
  });
  env.getEnvironment(function(err, environment) {
	  var defaultLanguage = (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) ? chrome.i18n.getUILanguage() : navigator.language;
	  language = environment.user ? environment.user.language : defaultLanguage;
	  webL10n.language.code = language;
  });

  function generateXOLogoWithColor(color) {
    var coloredLogo = xoLogo;
    coloredLogo = coloredLogo.replace("#010101", color.stroke)
    coloredLogo = coloredLogo.replace("#FFFFFF", color.fill)

    return "data:image/svg+xml;base64," + btoa(coloredLogo);
  }

  function displayConnectedPeopleHtml() {
    var presenceUsersDiv = document.getElementById("presence-users");
    var html = "

"
    for (var key in connectedPeople) {
      html += "
" + connectedPeople[key].name + "
"
    }
    html += "
"
    presenceUsersDiv.innerHTML = html
  }

  function displayConnectedPeople(users) {
    var presenceUsersDiv = document.getElementById("presence-users");
    if (!users || !presenceUsersDiv) {
      return;
    }
    connectedPeople = {};
	PaintApp.data.presence.listSharedActivityUsers(PaintApp.data.presence.getSharedInfo().id, function(usersConnected) {
		connectedPeople = {};
		for (var i = 0; i < usersConnected.length; i++) {
			var userConnected = usersConnected[i];
			connectedPeople[userConnected.networkId] = userConnected;
		}
		displayConnectedPeopleHtml();
	});
  }

  /* Function to handle user enter/exit  */
  function onSharedActivityUserChanged(msg) {
    var userName = msg.user.name.replace('<', '<').replace('>', '>');
    var html = ""
    if (msg.move === 1) {
      PaintApp.libs.humane.log(html + webL10n.get("PlayerJoin",{user: userName}))
    }

    if (msg.move === -1) {
      PaintApp.libs.humane.log(html + webL10n.get("PlayerLeave",{user: userName}))
    }

    PaintApp.data.presence.listSharedActivities(function(activities) {
      for (var i = 0; i < activities.length; i++) {
        if (activities[i].id === PaintApp.data.presence.getSharedInfo().id) {
          displayConnectedPeople(activities[i].users);
        }
      }
    });
  }

  function sendMessage(content) {
    var sharedId = window.top.sugar.environment.sharedId;
    if (!sharedId) {
      sharedId = PaintApp.data.presence.getSharedInfo().id
    }
    PaintApp.data.presence.sendMessage(sharedId, {
      user: PaintApp.data.presence.getUserInfo(),
      content: content
    });
  }

  /* Enabling an activity to be shared with the presenceObject */
  function shareActivity() {
    var activity = PaintApp.libs.activity;
    PaintApp.data.presence = activity.getPresenceObject(function(error, presence) {
      // Unable to join
      if (error) {
        console.log("error");
        return;
      }

      PaintApp.data.isShared = true;

      // Store settings
      userSettings = presence.getUserInfo();
      console.log("connected");

      // Not found, create a new shared activity
      if (!window.top.sugar.environment.sharedId) {
        presence.createSharedActivity('org.olpcfrance.PaintActivity', function(groupId) {});
      }

      // Show a disconnected message when the WebSocket is closed.
      presence.onConnectionClosed(function(event) {
        console.log(event);
        console.log("Connection closed");
      });

      // Display connection changed
      presence.onSharedActivityUserChanged(function(msg) {
        onSharedActivityUserChanged(msg);
      });

      // Handle messages received
      presence.onDataReceived(PaintApp.collaboration.onDataReceived);

      if (!PaintApp.data.isHost) {
        var lookForOtherUsersInterval = setInterval(function() {
            if (!PaintApp.data.presence.sharedInfo.users) {
              return;
            }
            displayConnectedPeople();
            if (!PaintApp.data.requestedData) {
              PaintApp.data.requestedData = true;
              sendMessage({
                action: "entranceToDataURLRequest " + PaintApp.data.presence.sharedInfo.users[0]
              })
            }
            clearInterval(lookForOtherUsersInterval)
          },
          500);
      }

    });
  }

  function compress(data) {
    if (PaintApp.data.isCompressEnabled) {
      return PaintApp.libs.lzstring.compressToUTF16(data);
    }
    return data;
  }

  function decompress(data) {
    if (PaintApp.data.isCompressEnabled) {
      return PaintApp.libs.lzstring.decompressFromUTF16(data);
    }
    return data;
  }

  function handlePath(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    ctx.beginPath();
    ctx.strokeStyle = msg.content.data.strokeStyle;
    ctx.lineCap = msg.content.data.lineCap;
    ctx.lineWidth = msg.content.data.lineWidth;
    ctx.moveTo(msg.content.data.from.x, msg.content.data.from.y);
    ctx.lineTo(msg.content.data.to.x, msg.content.data.to.y);
    ctx.stroke();
  }

  function handleText(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    ctx.font = msg.content.data.font;
    ctx.fillStyle = msg.content.data.fillStyle;
    ctx.textAlign = msg.content.data.textAlign;
    ctx.fillText(msg.content.data.text, msg.content.data.left, msg.content.data.top);

  }

  function handleDrawImage(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    var img = new Image();
    img.onload = function() {
      ctx.drawImage(img, msg.content.data.left, msg.content.data.top, msg.content.data.width, msg.content.data.height);
    };
    img.src = msg.content.data.src;
  }

  function handleDrawStamp(msg) {
    var platform = 'webkit';
    var isFirefox = typeof InstallTrigger !== 'undefined';
    if (isFirefox) {
      platform = 'gecko';
    }
    ctx = PaintApp.elements.canvas.getContext('2d');
    var stampURL = msg.content.data.stampBase.replace('{platform}', platform);
    var url = window.location.href.split('/');
    url.pop();
    url = url.join('/') + '/' + stampURL;
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.onload = function(e) {
      if (request.status === 200 || request.status === 0) {
        var stamp = PaintApp.modes.Stamp.changeColors(request.responseText, msg.content.data.color.fill, msg.content.data.color.stroke);
        var img = new Image();
        img.onload = function() {
          ctx.drawImage(img, msg.content.data.left, msg.content.data.top, msg.content.data.width, msg.content.data.height);
        };
        img.src = 'data:image/svg+xml;base64,' + btoa(stamp);
      }
    };
    request.send(null);
  }

  function handleEntranceToDataURL(msg) {
    PaintApp.data.entranceToDataURL = true;
    PaintApp.clearCanvas();
    img = new Image();
    img.onload = function() {
      PaintApp.elements.canvas.getContext('2d').drawImage(img, 0, 0, msg.content.data.width, msg.content.data.height);
    };
    img.src = decompress(msg.content.data.src);

  }

  function handleToDataURL(msg) {
    PaintApp.clearCanvas();
    img = new Image();
    img.onload = function() {
      PaintApp.elements.canvas.getContext('2d').drawImage(img, 0, 0, msg.content.data.width, msg.content.data.height);
    };
    img.src = decompress(msg.content.data.src);

  }

  function handleClearCanvas(msg) {
    PaintApp.clearCanvas();
  }

  function handleSaveCanvas(msg) {
    if (PaintApp.data.isHost) {
      PaintApp.saveCanvas();
    }
  }

  function handleEntranceToDataURLRequest(msg) {
    try {
      PaintApp.data.presence.sendMessage(PaintApp.data.presence.getSharedInfo().id, {
        user: PaintApp.data.presence.getUserInfo(),
        content: {
          action: 'entranceToDataURL',
          data: {
            width: PaintApp.elements.canvas.width / window.devicePixelRatio,
            height: PaintApp.elements.canvas.height / window.devicePixelRatio,
            src: compress(PaintApp.elements.canvas.toDataURL())
          }
        }
      });
    } catch (e) {}
  }

  /* Handle data reception in shared activity */
  function onDataReceived(msg) {
    /* Ignore messages coming from ourselves */
    if (PaintApp.data.presence.getUserInfo().networkId === msg.user.networkId) {
      return;
    }

    PaintApp.tmp = msg;

    var userName = msg.user.name.replace('<', '<').replace('>', '>');
    var me = 'entranceToDataURLRequest ' + PaintApp.data.presence.getUserInfo().networkId
    switch (msg.content.action) {

      /* Request to draw points/line */
      case 'path':
        handlePath(msg)
        break;

        /* Request to draw text */
      case 'text':
        handleText(msg)
        break;

        /* Request to draw image */
      case 'drawImage':
        handleDrawImage(msg)
        break;

        /* Request to draw stamp */
      case 'drawStamp':
        handleDrawStamp(msg)
        break;

      case me:
        handleEntranceToDataURLRequest(msg)
        break;

        /* When entering inside the collaboration mode this message will be used to get the current paint  */
      case 'entranceToDataURL':
        handleEntranceToDataURL(msg)
        break;

        /* Request to redraw the canvas  */
      case 'toDataURL':
        handleToDataURL(msg)
        break;

        /* Request to clear the canvas  */
      case 'clearCanvas':
        handleClearCanvas(msg)
        break;

        /* Request to save the canvas  */
      case 'saveCanvas':
        handleSaveCanvas(msg)

        break;
    }
  }

  return {
    onDataReceived: onDataReceived,
    compress: compress,
    decompress: decompress,
    shareActivity: shareActivity,
    sendMessage: sendMessage,
    displayConnectedPeople: displayConnectedPeople
  }

})
<commit_msg>Added presencepalette.popDown() on Paint Activity<commit_after>define(["sugar-web/env", "webL10n"], function (env, webL10n) {

  var connectedPeople = {};
  var xoLogo = '<?xml version="1.0" ?><!DOCTYPE svg  PUBLIC \'-//W3C//DTD SVG 1.1//EN\'  \'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\' [<!ENTITY stroke_color "#010101"><!ENTITY fill_color "#FFFFFF">]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="stock-xo_1_"><path d="M33.233,35.1l10.102,10.1c0.752,0.75,1.217,1.783,1.217,2.932   c0,2.287-1.855,4.143-4.146,4.143c-1.145,0-2.178-0.463-2.932-1.211L27.372,40.961l-10.1,10.1c-0.75,0.75-1.787,1.211-2.934,1.211   c-2.284,0-4.143-1.854-4.143-4.141c0-1.146,0.465-2.184,1.212-2.934l10.104-10.102L11.409,24.995   c-0.747-0.748-1.212-1.785-1.212-2.93c0-2.289,1.854-4.146,4.146-4.146c1.143,0,2.18,0.465,2.93,1.214l10.099,10.102l10.102-10.103   c0.754-0.749,1.787-1.214,2.934-1.214c2.289,0,4.146,1.856,4.146,4.145c0,1.146-0.467,2.18-1.217,2.932L33.233,35.1z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/><circle cx="27.371" cy="10.849" fill="&fill_color;" r="8.122" stroke="&stroke_color;" stroke-width="3.5"/></g></svg>';

  var language;
  window.addEventListener('localized', function(e) {
      if (e.language != language) {
          setTimeout(function() {
            webL10n.language.code = language;
          }, 50);
      }
  });
  env.getEnvironment(function(err, environment) {
	  var defaultLanguage = (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) ? chrome.i18n.getUILanguage() : navigator.language;
	  language = environment.user ? environment.user.language : defaultLanguage;
	  webL10n.language.code = language;
  });

  function generateXOLogoWithColor(color) {
    var coloredLogo = xoLogo;
    coloredLogo = coloredLogo.replace("#010101", color.stroke)
    coloredLogo = coloredLogo.replace("#FFFFFF", color.fill)

    return "data:image/svg+xml;base64," + btoa(coloredLogo);
  }

  function displayConnectedPeopleHtml() {
    var presenceUsersDiv = document.getElementById("presence-users");
    var html = "

"
    for (var key in connectedPeople) {
      html += "
" + connectedPeople[key].name + "
"
    }
    html += "
"
    presenceUsersDiv.innerHTML = html
  }

  function displayConnectedPeople(users) {
    var presenceUsersDiv = document.getElementById("presence-users");
    if (!users || !presenceUsersDiv) {
      return;
    }
    connectedPeople = {};
	PaintApp.data.presence.listSharedActivityUsers(PaintApp.data.presence.getSharedInfo().id, function(usersConnected) {
		connectedPeople = {};
		for (var i = 0; i < usersConnected.length; i++) {
			var userConnected = usersConnected[i];
			connectedPeople[userConnected.networkId] = userConnected;
		}
		displayConnectedPeopleHtml();
	});
  }

  /* Function to handle user enter/exit  */
  function onSharedActivityUserChanged(msg) {
    var userName = msg.user.name.replace('<', '<').replace('>', '>');
    var html = ""
    if (msg.move === 1) {
      PaintApp.libs.humane.log(html + webL10n.get("PlayerJoin",{user: userName}))
    }

    if (msg.move === -1) {
      PaintApp.libs.humane.log(html + webL10n.get("PlayerLeave",{user: userName}))
    }

    PaintApp.data.presence.listSharedActivities(function(activities) {
      for (var i = 0; i < activities.length; i++) {
        if (activities[i].id === PaintApp.data.presence.getSharedInfo().id) {
          displayConnectedPeople(activities[i].users);
        }
      }
    });
  }

  function sendMessage(content) {
    var sharedId = window.top.sugar.environment.sharedId;
    if (!sharedId) {
      sharedId = PaintApp.data.presence.getSharedInfo().id
    }
    PaintApp.data.presence.sendMessage(sharedId, {
      user: PaintApp.data.presence.getUserInfo(),
      content: content
    });
  }

  /* Enabling an activity to be shared with the presenceObject */
  function shareActivity() {
    var activity = PaintApp.libs.activity;
    PaintApp.data.presence = activity.getPresenceObject(function(error, presence) {
      presencepalette.popDown();
      // Unable to join
      if (error) {
        console.log("error");
        return;
      }

      PaintApp.data.isShared = true;

      // Store settings
      userSettings = presence.getUserInfo();
      console.log("connected");

      // Not found, create a new shared activity
      if (!window.top.sugar.environment.sharedId) {
        presence.createSharedActivity('org.olpcfrance.PaintActivity', function(groupId) {});
      }

      // Show a disconnected message when the WebSocket is closed.
      presence.onConnectionClosed(function(event) {
        console.log(event);
        console.log("Connection closed");
      });

      // Display connection changed
      presence.onSharedActivityUserChanged(function(msg) {
        onSharedActivityUserChanged(msg);
      });

      // Handle messages received
      presence.onDataReceived(PaintApp.collaboration.onDataReceived);

      if (!PaintApp.data.isHost) {
        var lookForOtherUsersInterval = setInterval(function() {
            if (!PaintApp.data.presence.sharedInfo.users) {
              return;
            }
            displayConnectedPeople();
            if (!PaintApp.data.requestedData) {
              PaintApp.data.requestedData = true;
              sendMessage({
                action: "entranceToDataURLRequest " + PaintApp.data.presence.sharedInfo.users[0]
              })
            }
            clearInterval(lookForOtherUsersInterval)
          },
          500);
      }

    });
  }

  function compress(data) {
    if (PaintApp.data.isCompressEnabled) {
      return PaintApp.libs.lzstring.compressToUTF16(data);
    }
    return data;
  }

  function decompress(data) {
    if (PaintApp.data.isCompressEnabled) {
      return PaintApp.libs.lzstring.decompressFromUTF16(data);
    }
    return data;
  }

  function handlePath(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    ctx.beginPath();
    ctx.strokeStyle = msg.content.data.strokeStyle;
    ctx.lineCap = msg.content.data.lineCap;
    ctx.lineWidth = msg.content.data.lineWidth;
    ctx.moveTo(msg.content.data.from.x, msg.content.data.from.y);
    ctx.lineTo(msg.content.data.to.x, msg.content.data.to.y);
    ctx.stroke();
  }

  function handleText(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    ctx.font = msg.content.data.font;
    ctx.fillStyle = msg.content.data.fillStyle;
    ctx.textAlign = msg.content.data.textAlign;
    ctx.fillText(msg.content.data.text, msg.content.data.left, msg.content.data.top);

  }

  function handleDrawImage(msg) {
    ctx = PaintApp.elements.canvas.getContext('2d');
    var img = new Image();
    img.onload = function() {
      ctx.drawImage(img, msg.content.data.left, msg.content.data.top, msg.content.data.width, msg.content.data.height);
    };
    img.src = msg.content.data.src;
  }

  function handleDrawStamp(msg) {
    var platform = 'webkit';
    var isFirefox = typeof InstallTrigger !== 'undefined';
    if (isFirefox) {
      platform = 'gecko';
    }
    ctx = PaintApp.elements.canvas.getContext('2d');
    var stampURL = msg.content.data.stampBase.replace('{platform}', platform);
    var url = window.location.href.split('/');
    url.pop();
    url = url.join('/') + '/' + stampURL;
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.onload = function(e) {
      if (request.status === 200 || request.status === 0) {
        var stamp = PaintApp.modes.Stamp.changeColors(request.responseText, msg.content.data.color.fill, msg.content.data.color.stroke);
        var img = new Image();
        img.onload = function() {
          ctx.drawImage(img, msg.content.data.left, msg.content.data.top, msg.content.data.width, msg.content.data.height);
        };
        img.src = 'data:image/svg+xml;base64,' + btoa(stamp);
      }
    };
    request.send(null);
  }

  function handleEntranceToDataURL(msg) {
    PaintApp.data.entranceToDataURL = true;
    PaintApp.clearCanvas();
    img = new Image();
    img.onload = function() {
      PaintApp.elements.canvas.getContext('2d').drawImage(img, 0, 0, msg.content.data.width, msg.content.data.height);
    };
    img.src = decompress(msg.content.data.src);

  }

  function handleToDataURL(msg) {
    PaintApp.clearCanvas();
    img = new Image();
    img.onload = function() {
      PaintApp.elements.canvas.getContext('2d').drawImage(img, 0, 0, msg.content.data.width, msg.content.data.height);
    };
    img.src = decompress(msg.content.data.src);

  }

  function handleClearCanvas(msg) {
    PaintApp.clearCanvas();
  }

  function handleSaveCanvas(msg) {
    if (PaintApp.data.isHost) {
      PaintApp.saveCanvas();
    }
  }

  function handleEntranceToDataURLRequest(msg) {
    try {
      PaintApp.data.presence.sendMessage(PaintApp.data.presence.getSharedInfo().id, {
        user: PaintApp.data.presence.getUserInfo(),
        content: {
          action: 'entranceToDataURL',
          data: {
            width: PaintApp.elements.canvas.width / window.devicePixelRatio,
            height: PaintApp.elements.canvas.height / window.devicePixelRatio,
            src: compress(PaintApp.elements.canvas.toDataURL())
          }
        }
      });
    } catch (e) {}
  }

  /* Handle data reception in shared activity */
  function onDataReceived(msg) {
    /* Ignore messages coming from ourselves */
    if (PaintApp.data.presence.getUserInfo().networkId === msg.user.networkId) {
      return;
    }

    PaintApp.tmp = msg;

    var userName = msg.user.name.replace('<', '<').replace('>', '>');
    var me = 'entranceToDataURLRequest ' + PaintApp.data.presence.getUserInfo().networkId
    switch (msg.content.action) {

      /* Request to draw points/line */
      case 'path':
        handlePath(msg)
        break;

        /* Request to draw text */
      case 'text':
        handleText(msg)
        break;

        /* Request to draw image */
      case 'drawImage':
        handleDrawImage(msg)
        break;

        /* Request to draw stamp */
      case 'drawStamp':
        handleDrawStamp(msg)
        break;

      case me:
        handleEntranceToDataURLRequest(msg)
        break;

        /* When entering inside the collaboration mode this message will be used to get the current paint  */
      case 'entranceToDataURL':
        handleEntranceToDataURL(msg)
        break;

        /* Request to redraw the canvas  */
      case 'toDataURL':
        handleToDataURL(msg)
        break;

        /* Request to clear the canvas  */
      case 'clearCanvas':
        handleClearCanvas(msg)
        break;

        /* Request to save the canvas  */
      case 'saveCanvas':
        handleSaveCanvas(msg)

        break;
    }
  }

  return {
    onDataReceived: onDataReceived,
    compress: compress,
    decompress: decompress,
    shareActivity: shareActivity,
    sendMessage: sendMessage,
    displayConnectedPeople: displayConnectedPeople
  }

})
<|endoftext|>