<commit_before>class GridImager {
  constructor(options) {
    this.cssFiles_ = new Map();
    this.styleString_ = '';
    this.filter_ = options.filter || (_ => true);
    this.scale_ = options.scale || 1;
    this.disableSmoothing_ = options.disableSmoothing || false;
    this.imageElementContainer_ = null;
  }

  addCssFile(path) {
    this.cssFiles_.set(path, '');
    return new Promise((resolve, reject) => {
      this.loadFile_(path, fileContent => {
        this.replaceInlinedSvgWithPng_(fileContent).then(processedCss => {
          // Verify the entry still exists, i.e. that it wasn't deleted by a
          // theme change.
          if (this.cssFiles_.has(path)) {
            this.cssFiles_.set(path, processedCss);
          }
          resolve();
        });
      });
    });
  }

  removeCssFile(path) {
    this.cssFiles_.delete(path);
  }

  recalculateStyleString() {
    this.styleString_ = Array.from(this.cssFiles_.entries())
        .map(([path, fileContent]) =>
          `<style><!-- ${path} -->\n${fileContent}</style>`).join('\n')
        .replace(/\s+/g, ' ');
  }

  async node2svgElement(node) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, false);
    return await this.foreignObjectString2svgElement_(foreignObjectString);
  }

  async node2svgDataUrl(node, width, height) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    return await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
  }

  async node2pngDataUrl(node, width, height) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    const imageElement = await this.foreignObjectString2imageElement_(
        foreignObjectString, width, height);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  msSince_(start) {
    return `${Math.round(performance.now() - start)}ms`;
  }

  async node2xml_(node) {
    const start = performance.now();
    const needsCloning = node.innerHTML.includes('data:image/svg+xml;');
    let actualNode = node;
    let cloneContainer = null;
    if (needsCloning) {
      cloneContainer = document.createElement('div');
      cloneContainer.style.display = 'none';
      node.parentElement.appendChild(cloneContainer);
      actualNode = await this.cloneNode_(node, cloneContainer);
      debug(`node cloning done in ${this.msSince_(start)}`);
    }
    const serializeStart = performance.now();
    const result = new XMLSerializer().serializeToString(actualNode);
    debug(`node serialization done in ${this.msSince_(serializeStart)}`);
    if (needsCloning) {
      node.parentElement.removeChild(cloneContainer);
    }
    debug(`node2xml_() done in ${this.msSince_(start)}`);
    return result;
  }

  async xml2foreignObjectString_(xml, includeStyle) {
    return '<foreignObject style="width:100%;height:100%;">' +
      (includeStyle ? this.styleString_ : '') + xml + '</foreignObject>'
        .replace(/\n/g, ' ')
        .replace(/ +/g, ' ');
  }

  async foreignObjectString2svgElement_(foreignObjectString) {
    const start = performance.now();
    const svgElement = document.createElement('svg');
    svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgElement.innerHTML = foreignObjectString;
    debug(`foreignObjectString2svgElement_() done in ${this.msSince_(start)}`);
    return svgElement;
  }

  async foreignObjectString2svgDataUrl_(foreignObjectString, width, height) {
    const encodedForeignObjectString = encodeURIComponent(foreignObjectString);
    return 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" ' +
        `width="${width}" height="${height}">${encodedForeignObjectString}` +
        '</svg>';
  }

  async foreignObjectString2imageElement_(foreignObjectString, width, height) {
    const svgDataUrl = await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
    return await this.dataUrl2imageElement_(svgDataUrl);
  }

  async dataUrl2imageElement_(dataUrl) {
    const imageElement = document.createElement('img');
    imageElement.src = dataUrl;
    return imageElement;
  }

  async imageElement2canvas_(imageElement, width, height) {
    return new Promise((resolve, reject) => {
      const start = performance.now();
      const canvas = document.createElement('canvas');
      canvas.style.width = width;
      canvas.style.height = height;
      canvas.width = this.scale_ * width;
      canvas.height = this.scale_ * height;
      const context = canvas.getContext('2d');
      context.scale(this.scale_, this.scale_);
      if (this.disableSmoothing_) {
        context.imageSmoothingEnabled = false;
      }
      if (!this.imageElementContainer_) {
        const parent = document.getElementById('mapContainer');
        this.imageElementContainer_ =
            createAndAppendDivWithClass(parent, 'grid-imager-image-container');
      }
      imageElement.addEventListener('load', () => {
        const startDraw = performance.now();
        context.drawImage(imageElement, 0, 0);
        debug(`drawing image on canvas done in ${this.msSince_(startDraw)}`);
        this.imageElementContainer_.removeChild(imageElement);
        debug(`imageElement2canvas_() done in ${this.msSince_(start)}`);
        resolve(canvas);
      });
      this.imageElementContainer_.appendChild(imageElement);
    });
  }

  async canvas2dataUrl_(canvas) {
    const start = performance.now();
    const pngDataUrl = canvas.toDataURL();
    debug(`canvas2dataUrl_() done in ${this.msSince_(start)}`);
    return pngDataUrl;
  }

  async svgDataUrl2pngDataUrl_(svgDataUrl, width, height) {
    const imageElement = await this.dataUrl2imageElement_(svgDataUrl);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  loadFile_(path, callback) {
    const xhr = new XMLHttpRequest();
    xhr.open('get', path, true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState != 4) return;
      callback(xhr.responseText);
    };
    xhr.send();
  }

  async replaceInlinedSvgWithPng_(text) {
    const regex =
        /url\((\\?"|"|')(data:image\/svg\+xml;.*\/svg *>)(\\?"|"|')\);/g;
    const inlinedSvgs = [];
    let match = null;
    while (match = regex.exec(text)) {
      const quoteLength = match[1].length;
      const svgDataUrl = match[2];
      const begin = match.index + 4 + quoteLength;
      const decodedSvgDataUrl = svgDataUrl
          .replace(/&/g, '&')
          .replace(/\\?"/g, '"')
          .replace(/</g, '<')
          .replace(/>/g, '>');
      inlinedSvgs.push({
        begin,
        end: begin + svgDataUrl.length,
        str: decodedSvgDataUrl,
      });
    }
    let result = '';
    let lastIndex = 0;
    for (const inlinedSvg of inlinedSvgs) {
      result += text.substring(lastIndex, inlinedSvg.begin);
      const {width, height} =
          this.extractDimensionsFromSvgStr_(inlinedSvg.str);
      const pngDataUrl =
          await this.svgDataUrl2pngDataUrl_(inlinedSvg.str, width, height);
      result += pngDataUrl;
      lastIndex = inlinedSvg.end;
    };
    result += text.substring(lastIndex);
    return result;
  }

  async cloneNode_(node, parent) {
    const cloned = document.createElement(node.tag);
    parent.appendChild(cloned);
    cloned.outerHTML = await this.replaceInlinedSvgWithPng_(node.outerHTML);
    return parent.children[0];
  }

  extractDimensionsFromSvgStr_(svgDataUrl) {
    let width = 0;
    let height = 0;
    svgDataUrl.replace(/<svg xmlns=.http:\/\/www\.w3\.org\/2000\/svg.[^>]*width=.(\d+).[^>]*height=.(\d+)./, (match, group1, group2) => {
      width = group1;
      height = group2;
      return match;
    });
    return {width, height};
  }
}
<commit_msg>Small formatting fixes.<commit_after>const INLINE_SVG_REGEX =
    /url\((\\?"|"|')(data:image\/svg\+xml;.*\/svg *>)(\\?"|"|')\);/g;
const EXTRACT_DIMENSIONS_REGEX =
    /<svg[^>]*width=.(\d+).[^>]*height=.(\d+)./;

class GridImager {
  constructor(options) {
    this.cssFiles_ = new Map();
    this.styleString_ = '';
    this.filter_ = options.filter || (_ => true);
    this.scale_ = options.scale || 1;
    this.disableSmoothing_ = options.disableSmoothing || false;
    this.imageElementContainer_ = null;
  }

  addCssFile(path) {
    this.cssFiles_.set(path, '');
    return new Promise((resolve, reject) => {
      this.loadFile_(path, fileContent => {
        this.replaceInlinedSvgWithPng_(fileContent).then(processedCss => {
          // Verify the entry still exists, i.e. that it wasn't deleted by a
          // theme change.
          if (this.cssFiles_.has(path)) {
            this.cssFiles_.set(path, processedCss);
          }
          resolve();
        });
      });
    });
  }

  removeCssFile(path) {
    this.cssFiles_.delete(path);
  }

  recalculateStyleString() {
    this.styleString_ = Array.from(this.cssFiles_.entries())
        .map(([path, fileContent]) =>
          `<style><!-- ${path} -->\n${fileContent}</style>`).join('\n')
        .replace(/\s+/g, ' ');
  }

  async node2svgElement(node) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, false);
    return await this.foreignObjectString2svgElement_(foreignObjectString);
  }

  async node2svgDataUrl(node, width, height) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    return await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
  }

  async node2pngDataUrl(node, width, height) {
    const xml = await this.node2xml_(node);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    const imageElement = await this.foreignObjectString2imageElement_(
        foreignObjectString, width, height);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  msSince_(start) {
    return `${Math.round(performance.now() - start)}ms`;
  }

  async node2xml_(node) {
    const start = performance.now();
    const needsCloning = node.innerHTML.includes('data:image/svg+xml;');
    let actualNode = node;
    let cloneContainer = null;
    if (needsCloning) {
      cloneContainer = document.createElement('div');
      cloneContainer.style.display = 'none';
      node.parentElement.appendChild(cloneContainer);
      actualNode = await this.cloneNode_(node, cloneContainer);
      debug(`node cloning done in ${this.msSince_(start)}`);
    }
    const serializeStart = performance.now();
    const result = new XMLSerializer().serializeToString(actualNode);
    debug(`node serialization done in ${this.msSince_(serializeStart)}`);
    if (needsCloning) {
      node.parentElement.removeChild(cloneContainer);
    }
    debug(`node2xml_() done in ${this.msSince_(start)}`);
    return result;
  }

  async xml2foreignObjectString_(xml, includeStyle) {
    return '<foreignObject x="0" y="0" width="100%" height="100%">' +
      '<html xmlns="http://www.w3.org/1999/xhtml">' +
      (includeStyle ? this.styleString_ : '') + xml + '</html></foreignObject>'
        .replace(/\n/g, ' ')
        .replace(/ +/g, ' ');
  }

  async foreignObjectString2svgElement_(foreignObjectString) {
    const start = performance.now();
    const svgElement = document.createElement('svg');
    svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgElement.innerHTML = foreignObjectString;
    debug(`foreignObjectString2svgElement_() done in ${this.msSince_(start)}`);
    return svgElement;
  }

  async foreignObjectString2svgDataUrl_(foreignObjectString, width, height) {
    const encodedForeignObjectString = encodeURIComponent(foreignObjectString);
    return 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" ' +
        `width="${width}" height="${height}">${encodedForeignObjectString}` +
        '</svg>';
  }

  async foreignObjectString2imageElement_(foreignObjectString, width, height) {
    const svgDataUrl = await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
    return await this.dataUrl2imageElement_(svgDataUrl);
  }

  async dataUrl2imageElement_(dataUrl) {
    const imageElement = document.createElement('img');
    imageElement.src = dataUrl;
    return imageElement;
  }

  async imageElement2canvas_(imageElement, width, height) {
    return new Promise((resolve, reject) => {
      const start = performance.now();
      const canvas = document.createElement('canvas');
      canvas.style.width = width;
      canvas.style.height = height;
      canvas.width = this.scale_ * width;
      canvas.height = this.scale_ * height;
      const context = canvas.getContext('2d');
      context.scale(this.scale_, this.scale_);
      if (this.disableSmoothing_) {
        context.imageSmoothingEnabled = false;
      }
      if (!this.imageElementContainer_) {
        const parent = document.getElementById('mapContainer');
        this.imageElementContainer_ =
            createAndAppendDivWithClass(parent, 'grid-imager-image-container');
      }
      imageElement.addEventListener('load', () => {
        const startDraw = performance.now();
        context.drawImage(imageElement, 0, 0);
        debug(`drawing image on canvas done in ${this.msSince_(startDraw)}`);
        this.imageElementContainer_.removeChild(imageElement);
        debug(`imageElement2canvas_() done in ${this.msSince_(start)}`);
        resolve(canvas);
      });
      this.imageElementContainer_.appendChild(imageElement);
    });
  }

  async canvas2dataUrl_(canvas) {
    const start = performance.now();
    const pngDataUrl = canvas.toDataURL();
    debug(`canvas2dataUrl_() done in ${this.msSince_(start)}`);
    return pngDataUrl;
  }

  async svgDataUrl2pngDataUrl_(svgDataUrl, width, height) {
    const imageElement = await this.dataUrl2imageElement_(svgDataUrl);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  loadFile_(path, callback) {
    const xhr = new XMLHttpRequest();
    xhr.open('get', path, true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState != 4) return;
      callback(xhr.responseText);
    };
    xhr.send();
  }

  async replaceInlinedSvgWithPng_(text) {
    const inlinedSvgs = [];
    let match = null;
    while (match = INLINE_SVG_REGEX.exec(text)) {
      const quoteLength = match[1].length;
      const svgDataUrl = match[2];
      const begin = match.index + 4 + quoteLength;
      const decodedSvgDataUrl = svgDataUrl
          .replace(/&/g, '&')
          .replace(/\\?"/g, '"')
          .replace(/</g, '<')
          .replace(/>/g, '>');
      inlinedSvgs.push({
        begin,
        end: begin + svgDataUrl.length,
        str: decodedSvgDataUrl,
      });
    }
    let result = '';
    let lastIndex = 0;
    for (const inlinedSvg of inlinedSvgs) {
      result += text.substring(lastIndex, inlinedSvg.begin);
      const {width, height} =
          this.extractDimensionsFromSvgStr_(inlinedSvg.str);
      const pngDataUrl =
          await this.svgDataUrl2pngDataUrl_(inlinedSvg.str, width, height);
      result += pngDataUrl;
      lastIndex = inlinedSvg.end;
    };
    result += text.substring(lastIndex);
    return result;
  }

  async cloneNode_(node, parent) {
    const cloned = document.createElement(node.tag);
    parent.appendChild(cloned);
    cloned.outerHTML = await this.replaceInlinedSvgWithPng_(node.outerHTML);
    return parent.children[0];
  }

  extractDimensionsFromSvgStr_(svgDataUrl) {
    let width = 0;
    let height = 0;
    svgDataUrl.replace(EXTRACT_DIMENSIONS_REGEX, (match, group1, group2) => {
      width = group1;
      height = group2;
      return match;
    });
    return {width, height};
  }
}
<|endoftext|>


<commit_before>//jQuery plugin to handle oneToMany tables
(function($){

	//public methods
	var methods = {
		init: function(options){
			return this.each(function(){
				var t = this;
				var $this = $(this);
				var data = {
					i: 0,
					clone_row: $this.find('.mto-row:first').detach()
				};

				//Bind add/delete events
				data.clone_row.find('.delete, .js-delete').click(function(e){
					e.preventDefault();
					methods.delete_row.apply(t, [$(this).parents('.mto-row')]);
				});

				if(options && options.methods){
					$.extend(methods, options.methods);
				}

				$this.find('.add, .js-add').data({
					methods: methods,
					$plugin: $this
				}).bind('click', {t: t}, methods.handle_add_click);

				$this.data('manyToOneSelector', data);
			});
		},

		//Creates a new row from data.
		inject : function(new_data){
			var $new_row = methods.add_row.call(this);
			for(var i = 0; i < new_data.length; i++){
				var $form_elem = $new_row.find(new_data[i].selector);
				if($form_elem.length === 0 && $.app.debug){
					console.error('No input found for selector "'+ new_data[i].selector +'" in $.fn.manyToOneSelector.inject');
				}
				if(typeof new_data[i].before === 'function'){
					new_data[i].before($new_row);
				}
				$form_elem.val(new_data[i].val);
				if(typeof new_data[i].after === 'function'){
					new_data[i].after($new_row);
				}
			}
			return $new_row;
		},

		add_row : function(){
			var $this = $(this);
			//Create new row, update counters and append it
			var data = $this.data('manyToOneSelector');
			var $new_row = data.clone_row.clone(true);
			var new_index = data['i'];
			data['i'] += 1;
			$this.data('manyToOneSelector', data);

			$new_row.attr('data-i', new_index);

			//update attributes
			var attr = ['name', 'id', 'for', 'value', 'data-id'];
			$new_row.find('[' + attr.join('], [') + ']').each(function(){
				for(var i = 0; i < attr.length; i++){
					var $elem = $(this);
					if($elem.attr(attr[i])){
						$elem.attr(attr[i], $elem.attr(attr[i]).replace('__PH__', new_index));
					}
				}
			});
			$this.find('.mto-container').append($new_row);

			//Update altrow (odd classes)
			methods.update_altrow.call(this);

			//do not put focus on first text field here cuz it may cause strange things in a new form.
			//Return row so caller can change it.
			return $new_row;
		},

		delete_row: function($row){
			//if theres only one row, clear it's data. else, delete it.
			if($row.siblings('.mto-row').length === 0){
				$row.find('input').each(function(i, input){
					var $input = $(input);
					if(typeof $input.data('textboxlist_ref') != 'undefined'){
						$input.data('textboxlist_ref').removeList();
					}
				});
				$row.find('input, textarea').not('.no-clear').val('').trigger('change');
				$row.find('select:not(.no-clear)').find('option:first').attr('selected', 'selected').trigger('change');
				$row.find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
			}
			else{
				// This allow us ot trigger the bit event bitRemove properly.
				$row.find('input').each(function(i, input){
					var $input = $(input);
					if(typeof $input.data('textboxlist_ref') != 'undefined'){
						$input.data('textboxlist_ref').removeList();
					}
				});
				if ($row.next().length) $row.next().find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
				else if ($row.prev().length) $row.prev().find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
				$row.remove();
				methods.update_altrow.call(this);
				methods.ensure_radio_checked.call(this);
			}
		},

		/**
		 * Removes ALL row from this MTO.
		 */
		delete_all_row: function(){
			methods.each_row.call(this, function(i, row){
				$(row).remove();
			});
		},

		update_altrow : function(){
			var odd = false;
			$(this).find('.mto-row').each(function(){
				if(odd) $(this).addClass('odd');
				else $(this).removeClass('odd');
				odd = !odd;
			});
		},

		ensure_radio_checked: function(){
			var $radios = $(this).find('input[type=radio]');
			if(!$radios.length) return;
			if($radios.filter(':checked').length) return
			$radios.first().prop('checked', true).trigger('click');
		},

		handle_add_click: function(e){
			var t = e.data.t;
			e.preventDefault();
			var $row = methods.add_row.apply(t);
			$row.find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
		},

		each_row: function(fn){
			return $(this).find('.mto-row').each(fn);
		},

		map_row: function(fn){
			return $(this).find('.mto-row').map(fn);
		},

		last_row: function(){
			return $(this).find('.mto-row').last();
		},

		is_empty: function($row){
			var is_empty = true;
			$(this).find('.mto-row').each(function(index, element){
				$row = $(element);
				$row.find('input, textarea').not('.no-clear').each(function(index, element){
					var $element = $(element);
					if($element.val() != ''){
						is_empty = false;
						return false;
					}
				});
				if(is_empty){
					$row.find('select:not(.no-clear)').each(function(index, element){
						if(element.selectedIndex != 0){
							is_empty = false;
							return false;
						}
					});
				}

				return is_empty;
			});

			return is_empty;
		}
	};

	$.fn.manyToOneSelector = function( method ) {
		if ( methods[method] ) {
			if(this.length > 1){
				throw '$.manyToOneSelector functions may only be called with one dom node (except for init)';
			}
	    return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
	  } else if ( typeof method === 'object' || ! method ) {
	    return methods.init.apply( this, arguments );
	  } else {
	    $.error( 'Method ' +  method + ' does not exist on jQuery.manyToOneSelector' );
	  }
	};
})(jQuery)
<commit_msg>jQuery OneToMany - Possibilité d'empecher focus sur premier element via une classe CSS.<commit_after>//jQuery plugin to handle oneToMany tables
(function($){

	//public methods
	var methods = {
		init: function(options){
			return this.each(function(){
				var t = this;
				var $this = $(this);
				var data = {
					i: 0,
					clone_row: $this.find('.mto-row:first').detach()
				};

				//Bind add/delete events
				data.clone_row.find('.delete, .js-delete').click(function(e){
					e.preventDefault();
					methods.delete_row.apply(t, [$(this).parents('.mto-row')]);
				});

				if(options && options.methods){
					$.extend(methods, options.methods);
				}

				$this.find('.add, .js-add').data({
					methods: methods,
					$plugin: $this
				}).bind('click', {t: t}, methods.handle_add_click);

				$this.data('manyToOneSelector', data);
			});
		},

		//Creates a new row from data.
		inject : function(new_data){
			var $new_row = methods.add_row.call(this);
			for(var i = 0; i < new_data.length; i++){
				var $form_elem = $new_row.find(new_data[i].selector);
				if($form_elem.length === 0 && $.app.debug){
					console.error('No input found for selector "'+ new_data[i].selector +'" in $.fn.manyToOneSelector.inject');
				}
				if(typeof new_data[i].before === 'function'){
					new_data[i].before($new_row);
				}
				$form_elem.val(new_data[i].val);
				if(typeof new_data[i].after === 'function'){
					new_data[i].after($new_row);
				}
			}
			return $new_row;
		},

		add_row : function(){
			var $this = $(this);
			//Create new row, update counters and append it
			var data = $this.data('manyToOneSelector');
			var $new_row = data.clone_row.clone(true);
			var new_index = data['i'];
			data['i'] += 1;
			$this.data('manyToOneSelector', data);

			$new_row.attr('data-i', new_index);

			//update attributes
			var attr = ['name', 'id', 'for', 'value', 'data-id'];
			$new_row.find('[' + attr.join('], [') + ']').each(function(){
				for(var i = 0; i < attr.length; i++){
					var $elem = $(this);
					if($elem.attr(attr[i])){
						$elem.attr(attr[i], $elem.attr(attr[i]).replace('__PH__', new_index));
					}
				}
			});
			$this.find('.mto-container').append($new_row);

			//Update altrow (odd classes)
			methods.update_altrow.call(this);

			//do not put focus on first text field here cuz it may cause strange things in a new form.
			//Return row so caller can change it.
			return $new_row;
		},

		delete_row: function($row){
			//if theres only one row, clear it's data. else, delete it.
			if($row.siblings('.mto-row').length === 0){
				$row.find('input').each(function(i, input){
					var $input = $(input);
					if(typeof $input.data('textboxlist_ref') != 'undefined'){
						$input.data('textboxlist_ref').removeList();
					}
				});
				$row.find('input, textarea').not('.no-clear').val('').trigger('change');
				$row.find('select:not(.no-clear)').find('option:first').attr('selected', 'selected').trigger('change');
				$row.find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
			}
			else{
				// This allow us ot trigger the bit event bitRemove properly.
				$row.find('input').each(function(i, input){
					var $input = $(input);
					if(typeof $input.data('textboxlist_ref') != 'undefined'){
						$input.data('textboxlist_ref').removeList();
					}
				});
				if ($row.next().length) $row.next().find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
				else if ($row.prev().length) $row.prev().find('input:tabbable,select:tabbable,textarea:tabbable').first().focus();
				$row.remove();
				methods.update_altrow.call(this);
				methods.ensure_radio_checked.call(this);
			}
		},

		/**
		 * Removes ALL row from this MTO.
		 */
		delete_all_row: function(){
			methods.each_row.call(this, function(i, row){
				$(row).remove();
			});
		},

		update_altrow : function(){
			var odd = false;
			$(this).find('.mto-row').each(function(){
				if(odd) $(this).addClass('odd');
				else $(this).removeClass('odd');
				odd = !odd;
			});
		},

		ensure_radio_checked: function(){
			var $radios = $(this).find('input[type=radio]');
			if(!$radios.length) return;
			if($radios.filter(':checked').length) return
			$radios.first().prop('checked', true).trigger('click');
		},

		handle_add_click: function(e){
			var t = e.data.t;
			e.preventDefault();
			var $row = methods.add_row.apply(t);
			$row.find('input:tabbable,select:tabbable,textarea:tabbable').not('.mto-nofocus').first().focus();
		},

		each_row: function(fn){
			return $(this).find('.mto-row').each(fn);
		},

		map_row: function(fn){
			return $(this).find('.mto-row').map(fn);
		},

		last_row: function(){
			return $(this).find('.mto-row').last();
		},

		is_empty: function($row){
			var is_empty = true;
			$(this).find('.mto-row').each(function(index, element){
				$row = $(element);
				$row.find('input, textarea').not('.no-clear').each(function(index, element){
					var $element = $(element);
					if($element.val() != ''){
						is_empty = false;
						return false;
					}
				});
				if(is_empty){
					$row.find('select:not(.no-clear)').each(function(index, element){
						if(element.selectedIndex != 0){
							is_empty = false;
							return false;
						}
					});
				}

				return is_empty;
			});

			return is_empty;
		}
	};

	$.fn.manyToOneSelector = function( method ) {
		if ( methods[method] ) {
			if(this.length > 1){
				throw '$.manyToOneSelector functions may only be called with one dom node (except for init)';
			}
	    return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
	  } else if ( typeof method === 'object' || ! method ) {
	    return methods.init.apply( this, arguments );
	  } else {
	    $.error( 'Method ' +  method + ' does not exist on jQuery.manyToOneSelector' );
	  }
	};
})(jQuery)
<|endoftext|>


<commit_before>module.exports = {

  forProjects: function(opts) {
    return Project.query(qb => {
      if (opts.user) {
        qb.leftJoin('projects_users', () => this.on('projects.id', '=', 'projects_users.project_id'));
        qb.where(() =>
          this.where('projects.user_id', opts.user)
          .orWhere('projects_users.user_id', opts.user));
      }

      if (opts.community) {
        qb.where('community_id', opts.community);
      }

      if (opts.published) {
        qb.whereRaw('published_at is not null');
      }

      qb.groupBy('projects.id');
    });
  },

  forPosts: function(opts) {
    return Post.query(function(qb) {

      qb.limit(opts.limit);
      qb.offset(opts.offset);
      qb.where({'post.active': true});

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('*, count(*) over () as total'));

      if (opts.users) {
        qb.whereIn('post.user_id', opts.users);
      }

      if (opts.excludeUsers) {
        qb.whereNotIn('post.user_id', opts.excludeUsers);
      }

      if (opts.communities) {
        qb.join('post_community', 'post_community.post_id', '=', 'post.id');
        qb.whereIn('post_community.community_id', opts.communities);
      }

      if (opts.project) {
        qb.join('posts_projects', 'posts_projects.post_id', '=', 'post.id');
        qb.where('posts_projects.project_id', opts.project);
      } else {
        qb.where('post.visibility', '!=', Post.Visibility.DRAFT_PROJECT);
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['post.name', 'post.description']
        });
      }

      if (opts.follower) {
        qb.join('follower', 'follower.post_id', '=', 'post.id');
        qb.where('follower.user_id', opts.follower);
        qb.whereRaw('(post.user_id != ? or post.user_id is null)', opts.follower);
      }

      if (!opts.type || opts.type === 'all') {
        qb.where('post.type', '!=', 'welcome');
      } else if (opts.type !== 'all+welcome') {
        qb.where({type: opts.type});
      }

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('((post.created_at between ? and ?) or (post.updated_at between ? and ?))',
          [opts.start_time, opts.end_time, opts.start_time, opts.end_time]);
      }

      if (opts.visibility) {
        qb.whereIn('visibility', opts.visibility);
      }

      if (opts.sort === 'suggested') {
        qb.join('user_post_relevance', 'user_post_relevance.post_id', '=', 'post.id');
        qb.where('user_post_relevance.user_id', opts.forUser);
        qb.orderBy('similarity', 'desc');
      } else if (opts.sort === 'fulfilled_at') {
        qb.orderByRaw('post.fulfilled_at desc, post.updated_at desc');
      } else if (opts.sort) {
        qb.orderBy(opts.sort, 'desc');
      }

    });
  },

  forUsers: function(opts) {
    return User.query(function(qb) {

      qb.limit(opts.limit || 1000);
      qb.offset(opts.offset || 0);
      qb.where("users.active", "=", true);

      // this is not necessarily what any consumer desires, but
      // some ordering must be specified for pagination
      qb.orderBy('name', 'asc');

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('count(users.*) over () as total'));

      if (opts.communities) {
        qb.join('users_community', 'users_community.user_id', '=', 'users.id');
        qb.whereIn('users_community.community_id', opts.communities);
        qb.where('users_community.active', true);
      }

      if (opts.project) {
        qb.join('projects_users', 'projects_users.user_id', '=', 'users.id');
        qb.leftJoin('projects', 'projects.user_id', '=', 'users.id');
        qb.where(() =>
          this.where('projects.id', opts.project)
          .orWhere('projects_users.project_id', opts.project));
      }

      if (opts.autocomplete) {
        qb.whereRaw("users.name ilike ?", opts.autocomplete + '%');
      }

      if (opts.term) {
        qb.leftJoin('users_skill', 'users_skill.user_id', '=', 'users.id');
        qb.leftJoin('users_org', 'users_org.user_id', '=', 'users.id');
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['users.name', 'users.bio', 'users_skill.skill_name', 'users_org.org_name']
        });
      }

      // prevent duplicates due to the joins
      qb.groupBy('users.id');

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('users.created_at between ? and ?', [opts.start_time, opts.end_time]);
      }

      if (opts.exclude) {
        qb.whereNotIn('id', opts.exclude);
      }
    });
  },

  addTermToQueryBuilder: function(term, qb, opts) {
    var query = _.chain(term.split(/\s*\s/)) // split on whitespace
      .map(word => word.replace(/[,;'|:&()!\\]+/, ''))
      .reject(_.isEmpty)
      .map(word => word + ':*') // add prefix matching
      .reduce((result, word) => {
        // build the tsquery string using logical AND operands
        result += " & " + word;
        return result;
      }).value(),

      statement = format('(%s)',
        opts.columns
        .map(col => format("(to_tsvector('english', %s) @@ to_tsquery(?))", col))
        .join(' or ')),

      values = _.times(opts.columns.length, () => query);

    qb.where(() => this.whereRaw(statement, values));
  }

}
<commit_msg>fix the search.forUsers to include users.*<commit_after>module.exports = {

  forProjects: function(opts) {
    return Project.query(qb => {
      if (opts.user) {
        qb.leftJoin('projects_users', () => this.on('projects.id', '=', 'projects_users.project_id'));
        qb.where(() =>
          this.where('projects.user_id', opts.user)
          .orWhere('projects_users.user_id', opts.user));
      }

      if (opts.community) {
        qb.where('community_id', opts.community);
      }

      if (opts.published) {
        qb.whereRaw('published_at is not null');
      }

      qb.groupBy('projects.id');
    });
  },

  forPosts: function(opts) {
    return Post.query(function(qb) {

      qb.limit(opts.limit);
      qb.offset(opts.offset);
      qb.where({'post.active': true});

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('*, count(*) over () as total'));

      if (opts.users) {
        qb.whereIn('post.user_id', opts.users);
      }

      if (opts.excludeUsers) {
        qb.whereNotIn('post.user_id', opts.excludeUsers);
      }

      if (opts.communities) {
        qb.join('post_community', 'post_community.post_id', '=', 'post.id');
        qb.whereIn('post_community.community_id', opts.communities);
      }

      if (opts.project) {
        qb.join('posts_projects', 'posts_projects.post_id', '=', 'post.id');
        qb.where('posts_projects.project_id', opts.project);
      } else {
        qb.where('post.visibility', '!=', Post.Visibility.DRAFT_PROJECT);
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['post.name', 'post.description']
        });
      }

      if (opts.follower) {
        qb.join('follower', 'follower.post_id', '=', 'post.id');
        qb.where('follower.user_id', opts.follower);
        qb.whereRaw('(post.user_id != ? or post.user_id is null)', opts.follower);
      }

      if (!opts.type || opts.type === 'all') {
        qb.where('post.type', '!=', 'welcome');
      } else if (opts.type !== 'all+welcome') {
        qb.where({type: opts.type});
      }

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('((post.created_at between ? and ?) or (post.updated_at between ? and ?))',
          [opts.start_time, opts.end_time, opts.start_time, opts.end_time]);
      }

      if (opts.visibility) {
        qb.whereIn('visibility', opts.visibility);
      }

      if (opts.sort === 'suggested') {
        qb.join('user_post_relevance', 'user_post_relevance.post_id', '=', 'post.id');
        qb.where('user_post_relevance.user_id', opts.forUser);
        qb.orderBy('similarity', 'desc');
      } else if (opts.sort === 'fulfilled_at') {
        qb.orderByRaw('post.fulfilled_at desc, post.updated_at desc');
      } else if (opts.sort) {
        qb.orderBy(opts.sort, 'desc');
      }

    });
  },

  forUsers: function(opts) {
    return User.query(function(qb) {

      qb.limit(opts.limit || 1000);
      qb.offset(opts.offset || 0);
      qb.where("users.active", "=", true);

      // this is not necessarily what any consumer desires, but
      // some ordering must be specified for pagination
      qb.orderBy('name', 'asc');

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('count(users.*) over () as total, users.*'));

      if (opts.communities) {
        qb.join('users_community', 'users_community.user_id', '=', 'users.id');
        qb.whereIn('users_community.community_id', opts.communities);
        qb.where('users_community.active', true);
      }

      if (opts.project) {
        qb.join('projects_users', 'projects_users.user_id', '=', 'users.id');
        qb.leftJoin('projects', 'projects.user_id', '=', 'users.id');
        qb.where(() =>
          this.where('projects.id', opts.project)
          .orWhere('projects_users.project_id', opts.project));
      }

      if (opts.autocomplete) {
        qb.whereRaw("users.name ilike ?", opts.autocomplete + '%');
      }

      if (opts.term) {
        qb.leftJoin('users_skill', 'users_skill.user_id', '=', 'users.id');
        qb.leftJoin('users_org', 'users_org.user_id', '=', 'users.id');
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['users.name', 'users.bio', 'users_skill.skill_name', 'users_org.org_name']
        });
      }

      // prevent duplicates due to the joins
      qb.groupBy('users.id');

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('users.created_at between ? and ?', [opts.start_time, opts.end_time]);
      }

      if (opts.exclude) {
        qb.whereNotIn('id', opts.exclude);
      }
    });
  },

  addTermToQueryBuilder: function(term, qb, opts) {
    var query = _.chain(term.split(/\s*\s/)) // split on whitespace
      .map(word => word.replace(/[,;'|:&()!\\]+/, ''))
      .reject(_.isEmpty)
      .map(word => word + ':*') // add prefix matching
      .reduce((result, word) => {
        // build the tsquery string using logical AND operands
        result += " & " + word;
        return result;
      }).value(),

      statement = format('(%s)',
        opts.columns
        .map(col => format("(to_tsvector('english', %s) @@ to_tsquery(?))", col))
        .join(' or ')),

      values = _.times(opts.columns.length, () => query);

    qb.where(() => this.whereRaw(statement, values));
  }

}
<|endoftext|>


<commit_before>import Ember from "ember";
import translations from "croodle/lang/translations";
/* global App */

export default Ember.View.extend({
  templateName: 'language-switch',

  languages: function() {
    var languages = [];
    for(var lang in translations) {
      languages.push(lang);
    }
    return languages;
  }.property(),

  languageChanged: function() {
    // change language
    var language = this.get('controller.language.selected');

    // save language in cookie
    document.cookie="language=" + language + ";" +
            // give it an lifetime of one year
            "max-age=" + 60*60*24*356 + ";";

    // rerender page
    App.reset();
  }.observes('controller.language.selected')
});
<commit_msg>fix namespace reference to own application<commit_after>import Ember from "ember";
import translations from "croodle/lang/translations";

export default Ember.View.extend({
  templateName: 'language-switch',

  languages: function() {
    var languages = [];
    for(var lang in translations) {
      languages.push(lang);
    }
    return languages;
  }.property(),

  languageChanged: function() {
    // change language
    var language = this.get('controller.language.selected');

    // save language in cookie
    document.cookie="language=" + language + ";" +
            // give it an lifetime of one year
            "max-age=" + 60*60*24*356 + ";";

    // rerender page
    Croodle.reset();
  }.observes('controller.language.selected')
});
<|endoftext|>


<commit_before>/*
	Plugin para validar y formatear un RUT Chileno
	Autor: www.kadumedia.com
	Mail:  hola@kadumedia.com
	Versión: 1.5
	*/
	;(function($){
		$.fn.rut = function(opt){
			var defaults = $.extend({
				error_html: 'Rut incorrecto',
				formatear : true,
				on : 'blur',
				required : true,
				placeholder : true,
				fn_error : function(input){
					mostrar_error(input, defaults.error_html);
				},
				fn_validado: function(input){}
			}, opt);
			return this.each(function(){
				var $t = $(this);
				$t.wrap('

');
				$t.attr('pattern', '[0-9]{7,8}-[0-9k]{1}');
				if(defaults.required) $t.attr('required', 'required');
				if(defaults.placeholder) $t.attr('placeholder', '12345678-5');
				if(defaults.formatear){
					$t.on('blur', function(){
						$t.val($.rut.formatear($t.val()));
					});
				}
				$t.on(defaults.on, function(){
					$('.rut-error').remove();
					if($.rut.validar($t.val()) && $.trim($t.val()) != '')
						defaults.fn_validado();
					else
						defaults.fn_error($t);
				});
			});
		}
		function mostrar_error(input, error){
			input.closest('.rut-container').append(error);
		}
	})(jQuery);
	jQuery.rut = {
		validar : function(rut){
			if (!/^[0-9]+-[0-9kK]{1}$/.test(rut))
				return false;
			var tmp = rut.split('-');
			var dv = tmp[1], rut 	= tmp[0];
			if(dv == 'K') dv = 'k';
			return ($.rut.dv(rut) == dv);
		},
		dv : function(rut){
			var M=0,S=1;
			for(;rut;rut=Math.floor(rut/10))
				S=(S+rut%10*(9-M++%6))%11;
			return S ? S-1 : 'k';
		},
		formatear : function(rut){
			return rut.replace(/^(\d{7,8})(\w{1})$/, '$1-$2');
		},
		quitar_formato : function(rut){
			rut = rut.split('-').join('').split('.').join('');
			return rut;
		}
	};
<commit_msg>Update jquery.rut.chileno.js<commit_after>/*
	Plugin para validar y formatear un RUT Chileno
	Autor: www.kadumedia.com
	Mail:  hola@kadumedia.com
	Versión: 1.5
	*/
	;(function($){
		$.fn.rut = function(opt){
			var defaults = $.extend({
				error_html: 'Rut incorrecto',
				formatear : true,
				on : 'blur',
				required : true,
				placeholder : true,
				fn_error : function(input){
					mostrar_error(input, defaults.error_html);
				},
				fn_validado: function(input){}
			}, opt);
			return this.each(function(){
				var $t = $(this);
				$t.wrap('

');
				$t.attr('pattern', '[0-9]{7,8}-[0-9k]{1}');
				if(defaults.required) $t.attr('required', 'required');
				if(defaults.placeholder) $t.attr('placeholder', '12345678-5');
				if(defaults.formatear){
					$t.on('blur', function(){
						$t.val($.rut.formatear($t.val()));
					});
				}
				$t.on(defaults.on, function(){
					$('.rut-error').remove();
					if($.rut.validar($t.val()) && $.trim($t.val()) != '')
						defaults.fn_validado($t);
					else
						defaults.fn_error($t);
				});
			});
		}
		function mostrar_error(input, error){
			input.closest('.rut-container').append(error);
		}
	})(jQuery);
	jQuery.rut = {
		validar : function(rut){
			if (!/^[0-9]+-[0-9kK]{1}$/.test(rut))
				return false;
			var tmp = rut.split('-');
			var dv = tmp[1], rut 	= tmp[0];
			if(dv == 'K') dv = 'k';
			return ($.rut.dv(rut) == dv);
		},
		dv : function(rut){
			var M=0,S=1;
			for(;rut;rut=Math.floor(rut/10))
				S=(S+rut%10*(9-M++%6))%11;
			return S ? S-1 : 'k';
		},
		formatear : function(rut){
			return rut.replace(/^(\d{7,8})(\w{1})$/, '$1-$2');
		},
		quitar_formato : function(rut){
			rut = rut.split('-').join('').split('.').join('');
			return rut;
		}
	};
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------------
// ImageManager
//
// The static class that loads images, creates bitmap objects and retains them.

function ImageManager() {
    throw new Error('This is a static class');
}

ImageManager.cache = new CacheMap(ImageManager);

ImageManager._imageCache = new ImageCache();
ImageManager._requestQueue = new RequestQueue();
ImageManager._systemReservationId = Utils.generateRuntimeId();

ImageManager._generateCacheKey = function(path, hue){
    return  path + ':' + hue;
};

ImageManager.loadAnimation = function(filename, hue) {
    return this.loadBitmap('img/animations/', filename, hue, true);
};

ImageManager.loadBattleback1 = function(filename, hue) {
    return this.loadBitmap('img/battlebacks1/', filename, hue, true);
};

ImageManager.loadBattleback2 = function(filename, hue) {
    return this.loadBitmap('img/battlebacks2/', filename, hue, true);
};

ImageManager.loadEnemy = function(filename, hue) {
    return this.loadBitmap('img/enemies/', filename, hue, true);
};

ImageManager.loadCharacter = function(filename, hue) {
    return this.loadBitmap('img/characters/', filename, hue, false);
};

ImageManager.loadFace = function(filename, hue) {
    return this.loadBitmap('img/faces/', filename, hue, true);
};

ImageManager.loadParallax = function(filename, hue) {
    return this.loadBitmap('img/parallaxes/', filename, hue, true);
};

ImageManager.loadPicture = function(filename, hue) {
    return this.loadBitmap('img/pictures/', filename, hue, true);
};

ImageManager.loadSvActor = function(filename, hue) {
    return this.loadBitmap('img/sv_actors/', filename, hue, false);
};

ImageManager.loadSvEnemy = function(filename, hue) {
    return this.loadBitmap('img/sv_enemies/', filename, hue, true);
};

ImageManager.loadSystem = function(filename, hue) {
    return this.loadBitmap('img/system/', filename, hue, false);
};

ImageManager.loadTileset = function(filename, hue) {
    return this.loadBitmap('img/tilesets/', filename, hue, false);
};

ImageManager.loadTitle1 = function(filename, hue) {
    return this.loadBitmap('img/titles1/', filename, hue, true);
};

ImageManager.loadTitle2 = function(filename, hue) {
    return this.loadBitmap('img/titles2/', filename, hue, true);
};

ImageManager.loadBitmap = function(folder, filename, hue, smooth) {
    if (filename) {
        var path = folder + encodeURIComponent(filename) + '.png';
        var bitmap = this.loadNormalBitmap(path, hue || 0);
        bitmap.smooth = smooth;
        return bitmap;
    } else {
        return this.loadEmptyBitmap();
    }
};

ImageManager.loadEmptyBitmap = function() {
    var empty = this._imageCache.get('empty');
    if(!empty){
        empty = new Bitmap();
        this._imageCache.add('empty', empty);
        this._imageCache.reserve('empty', empty, this._systemReservationId);
    }

    return empty;
};

ImageManager.loadNormalBitmap = function(path, hue) {
    hue = hue || 0;
    var key = this._generateCacheKey(path, hue);
    var bitmap = this._imageCache.get(key);
    if (!bitmap) {
        bitmap = Bitmap.load(path);
        bitmap.addLoadListener(function() {
            bitmap.rotateHue(hue);
        });
        this._imageCache.add(key, bitmap);
    }else if(!bitmap.isReady()){
        bitmap.decode();
    }

    return bitmap;
};

ImageManager.clear = function() {
    this._imageCache = new ImageCache();
};

ImageManager.isReady = function() {
    var bitmap = null;
    if(bitmap = this._imageCache.getErrorBitmap()){
        throw new Error('Failed to load: ' + bitmap.url);
    }
    return this._imageCache.isReady();
};

ImageManager.isObjectCharacter = function(filename) {
    var sign = filename.match(/^[\!\$]+/);
    return sign && sign[0].contains('!');
};

ImageManager.isBigCharacter = function(filename) {
    var sign = filename.match(/^[\!\$]+/);
    return sign && sign[0].contains('$');
};

ImageManager.isZeroParallax = function(filename) {
    return filename.charAt(0) === '!';
};

ImageManager.reserveAnimation = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/animations/', filename, hue, true, reservationId);
};

ImageManager.reserveBattleback1 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/battlebacks1/', filename, hue, true, reservationId);
};

ImageManager.reserveBattleback2 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/battlebacks2/', filename, hue, true, reservationId);
};

ImageManager.reserveEnemy = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/enemies/', filename, hue, true, reservationId);
};

ImageManager.reserveCharacter = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/characters/', filename, hue, false, reservationId);
};

ImageManager.reserveFace = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/faces/', filename, hue, true, reservationId);
};

ImageManager.reserveParallax = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/parallaxes/', filename, hue, true, reservationId);
};

ImageManager.reservePicture = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/pictures/', filename, hue, true, reservationId);
};

ImageManager.reserveSvActor = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/sv_actors/', filename, hue, false, reservationId);
};

ImageManager.reserveSvEnemy = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/sv_enemies/', filename, hue, true, reservationId);
};

ImageManager.reserveSystem = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/system/', filename, hue, false, reservationId || this._systemReservationId);
};

ImageManager.reserveTileset = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/tilesets/', filename, hue, false, reservationId);
};

ImageManager.reserveTitle1 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/titles1/', filename, hue, true, reservationId);
};

ImageManager.reserveTitle2 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/titles2/', filename, hue, true, reservationId);
};

ImageManager.reserveBitmap = function(folder, filename, hue, smooth, reservationId) {
    if (filename) {
        var path = folder + encodeURIComponent(filename) + '.png';
        var bitmap = this.reserveNormalBitmap(path, hue || 0, reservationId || this._defaultReservationId);
        bitmap.smooth = smooth;
        return bitmap;
    } else {
        return this.loadEmptyBitmap();
    }
};

ImageManager.reserveNormalBitmap = function(path, hue, reservationId){
    var bitmap = this.loadNormalBitmap(path, hue);
    this._imageCache.reserve(this._generateCacheKey(path, hue), bitmap, reservationId);

    return bitmap;
};

ImageManager.releaseReservation = function(reservationId){
    this._imageCache.releaseReservation(reservationId);
};

ImageManager.setDefaultReservationId = function(reservationId){
    this._defaultReservationId = reservationId;
};

ImageManager.requestAnimation = function(filename, hue) {
    return this.requestBitmap('img/animations/', filename, hue, true);
};

ImageManager.requestBattleback1 = function(filename, hue) {
    return this.requestBitmap('img/battlebacks1/', filename, hue, true);
};

ImageManager.requestBattleback2 = function(filename, hue) {
    return this.requestBitmap('img/battlebacks2/', filename, hue, true);
};

ImageManager.requestEnemy = function(filename, hue) {
    return this.requestBitmap('img/enemies/', filename, hue, true);
};

ImageManager.requestCharacter = function(filename, hue) {
    return this.requestBitmap('img/characters/', filename, hue, false);
};

ImageManager.requestFace = function(filename, hue) {
    return this.requestBitmap('img/faces/', filename, hue, true);
};

ImageManager.requestParallax = function(filename, hue) {
    return this.requestBitmap('img/parallaxes/', filename, hue, true);
};

ImageManager.requestPicture = function(filename, hue) {
    return this.requestBitmap('img/pictures/', filename, hue, true);
};

ImageManager.requestSvActor = function(filename, hue) {
    return this.requestBitmap('img/sv_actors/', filename, hue, false);
};

ImageManager.requestSvEnemy = function(filename, hue) {
    return this.requestBitmap('img/sv_enemies/', filename, hue, true);
};

ImageManager.requestSystem = function(filename, hue) {
    return this.requestBitmap('img/system/', filename, hue, false);
};

ImageManager.requestTileset = function(filename, hue) {
    return this.requestBitmap('img/tilesets/', filename, hue, false);
};

ImageManager.requestTitle1 = function(filename, hue) {
    return this.requestBitmap('img/titles1/', filename, hue, true);
};

ImageManager.requestTitle2 = function(filename, hue) {
    return this.requestBitmap('img/titles2/', filename, hue, true);
};

ImageManager.requestBitmap = function(folder, filename, hue, smooth) {
    var path = folder + encodeURIComponent(filename) + '.png';
    var bitmap = ImageManager.requestNormalBitmap(path, hue);
    bitmap.smooth = smooth;
    return bitmap;
};

ImageManager.requestNormalBitmap = function(path, hue){
    hue = hue || 0;
    var key = this._generateCacheKey(path, hue);
    var bitmap = this._imageCache.get(key);
    if(!bitmap){
        bitmap = Bitmap.request(path);
        bitmap.addLoadListener(function(){
            bitmap.rotateHue(hue);
        });
        this._imageCache.add(key, bitmap);
        this._requestQueue.enqueue(key, bitmap);
    }else{
        this._requestQueue.raisePriority(key);
    }

    return bitmap;
};

ImageManager.update = function(){
    this._requestQueue.update();
};

ImageManager.clearRequest = function(){
    this._requestQueue.clear();
};
<commit_msg>change checking hue<commit_after>//-----------------------------------------------------------------------------
// ImageManager
//
// The static class that loads images, creates bitmap objects and retains them.

function ImageManager() {
    throw new Error('This is a static class');
}

ImageManager.cache = new CacheMap(ImageManager);

ImageManager._imageCache = new ImageCache();
ImageManager._requestQueue = new RequestQueue();
ImageManager._systemReservationId = Utils.generateRuntimeId();

ImageManager._generateCacheKey = function(path, hue){
    return  path + ':' + hue;
};

ImageManager.loadAnimation = function(filename, hue) {
    return this.loadBitmap('img/animations/', filename, hue, true);
};

ImageManager.loadBattleback1 = function(filename, hue) {
    return this.loadBitmap('img/battlebacks1/', filename, hue, true);
};

ImageManager.loadBattleback2 = function(filename, hue) {
    return this.loadBitmap('img/battlebacks2/', filename, hue, true);
};

ImageManager.loadEnemy = function(filename, hue) {
    return this.loadBitmap('img/enemies/', filename, hue, true);
};

ImageManager.loadCharacter = function(filename, hue) {
    return this.loadBitmap('img/characters/', filename, hue, false);
};

ImageManager.loadFace = function(filename, hue) {
    return this.loadBitmap('img/faces/', filename, hue, true);
};

ImageManager.loadParallax = function(filename, hue) {
    return this.loadBitmap('img/parallaxes/', filename, hue, true);
};

ImageManager.loadPicture = function(filename, hue) {
    return this.loadBitmap('img/pictures/', filename, hue, true);
};

ImageManager.loadSvActor = function(filename, hue) {
    return this.loadBitmap('img/sv_actors/', filename, hue, false);
};

ImageManager.loadSvEnemy = function(filename, hue) {
    return this.loadBitmap('img/sv_enemies/', filename, hue, true);
};

ImageManager.loadSystem = function(filename, hue) {
    return this.loadBitmap('img/system/', filename, hue, false);
};

ImageManager.loadTileset = function(filename, hue) {
    return this.loadBitmap('img/tilesets/', filename, hue, false);
};

ImageManager.loadTitle1 = function(filename, hue) {
    return this.loadBitmap('img/titles1/', filename, hue, true);
};

ImageManager.loadTitle2 = function(filename, hue) {
    return this.loadBitmap('img/titles2/', filename, hue, true);
};

ImageManager.loadBitmap = function(folder, filename, hue, smooth) {
    if (filename) {
        var path = folder + encodeURIComponent(filename) + '.png';
        var bitmap = this.loadNormalBitmap(path, hue || 0);
        bitmap.smooth = smooth;
        return bitmap;
    } else {
        return this.loadEmptyBitmap();
    }
};

ImageManager.loadEmptyBitmap = function() {
    var empty = this._imageCache.get('empty');
    if(!empty){
        empty = new Bitmap();
        this._imageCache.add('empty', empty);
        this._imageCache.reserve('empty', empty, this._systemReservationId);
    }

    return empty;
};

ImageManager.loadNormalBitmap = function(path, hue) {
    var key = this._generateCacheKey(path, hue);
    var bitmap = this._imageCache.get(key);
    if (!bitmap) {
        bitmap = Bitmap.load(path);
        bitmap.addLoadListener(function() {
            bitmap.rotateHue(hue);
        });
        this._imageCache.add(key, bitmap);
    }else if(!bitmap.isReady()){
        bitmap.decode();
    }

    return bitmap;
};

ImageManager.clear = function() {
    this._imageCache = new ImageCache();
};

ImageManager.isReady = function() {
    var bitmap = null;
    if(bitmap = this._imageCache.getErrorBitmap()){
        throw new Error('Failed to load: ' + bitmap.url);
    }
    return this._imageCache.isReady();
};

ImageManager.isObjectCharacter = function(filename) {
    var sign = filename.match(/^[\!\$]+/);
    return sign && sign[0].contains('!');
};

ImageManager.isBigCharacter = function(filename) {
    var sign = filename.match(/^[\!\$]+/);
    return sign && sign[0].contains('$');
};

ImageManager.isZeroParallax = function(filename) {
    return filename.charAt(0) === '!';
};

ImageManager.reserveAnimation = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/animations/', filename, hue, true, reservationId);
};

ImageManager.reserveBattleback1 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/battlebacks1/', filename, hue, true, reservationId);
};

ImageManager.reserveBattleback2 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/battlebacks2/', filename, hue, true, reservationId);
};

ImageManager.reserveEnemy = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/enemies/', filename, hue, true, reservationId);
};

ImageManager.reserveCharacter = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/characters/', filename, hue, false, reservationId);
};

ImageManager.reserveFace = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/faces/', filename, hue, true, reservationId);
};

ImageManager.reserveParallax = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/parallaxes/', filename, hue, true, reservationId);
};

ImageManager.reservePicture = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/pictures/', filename, hue, true, reservationId);
};

ImageManager.reserveSvActor = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/sv_actors/', filename, hue, false, reservationId);
};

ImageManager.reserveSvEnemy = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/sv_enemies/', filename, hue, true, reservationId);
};

ImageManager.reserveSystem = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/system/', filename, hue, false, reservationId || this._systemReservationId);
};

ImageManager.reserveTileset = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/tilesets/', filename, hue, false, reservationId);
};

ImageManager.reserveTitle1 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/titles1/', filename, hue, true, reservationId);
};

ImageManager.reserveTitle2 = function(filename, hue, reservationId) {
    return this.reserveBitmap('img/titles2/', filename, hue, true, reservationId);
};

ImageManager.reserveBitmap = function(folder, filename, hue, smooth, reservationId) {
    if (filename) {
        var path = folder + encodeURIComponent(filename) + '.png';
        var bitmap = this.reserveNormalBitmap(path, hue || 0, reservationId || this._defaultReservationId);
        bitmap.smooth = smooth;
        return bitmap;
    } else {
        return this.loadEmptyBitmap();
    }
};

ImageManager.reserveNormalBitmap = function(path, hue, reservationId){
    var bitmap = this.loadNormalBitmap(path, hue);
    this._imageCache.reserve(this._generateCacheKey(path, hue), bitmap, reservationId);

    return bitmap;
};

ImageManager.releaseReservation = function(reservationId){
    this._imageCache.releaseReservation(reservationId);
};

ImageManager.setDefaultReservationId = function(reservationId){
    this._defaultReservationId = reservationId;
};

ImageManager.requestAnimation = function(filename, hue) {
    return this.requestBitmap('img/animations/', filename, hue, true);
};

ImageManager.requestBattleback1 = function(filename, hue) {
    return this.requestBitmap('img/battlebacks1/', filename, hue, true);
};

ImageManager.requestBattleback2 = function(filename, hue) {
    return this.requestBitmap('img/battlebacks2/', filename, hue, true);
};

ImageManager.requestEnemy = function(filename, hue) {
    return this.requestBitmap('img/enemies/', filename, hue, true);
};

ImageManager.requestCharacter = function(filename, hue) {
    return this.requestBitmap('img/characters/', filename, hue, false);
};

ImageManager.requestFace = function(filename, hue) {
    return this.requestBitmap('img/faces/', filename, hue, true);
};

ImageManager.requestParallax = function(filename, hue) {
    return this.requestBitmap('img/parallaxes/', filename, hue, true);
};

ImageManager.requestPicture = function(filename, hue) {
    return this.requestBitmap('img/pictures/', filename, hue, true);
};

ImageManager.requestSvActor = function(filename, hue) {
    return this.requestBitmap('img/sv_actors/', filename, hue, false);
};

ImageManager.requestSvEnemy = function(filename, hue) {
    return this.requestBitmap('img/sv_enemies/', filename, hue, true);
};

ImageManager.requestSystem = function(filename, hue) {
    return this.requestBitmap('img/system/', filename, hue, false);
};

ImageManager.requestTileset = function(filename, hue) {
    return this.requestBitmap('img/tilesets/', filename, hue, false);
};

ImageManager.requestTitle1 = function(filename, hue) {
    return this.requestBitmap('img/titles1/', filename, hue, true);
};

ImageManager.requestTitle2 = function(filename, hue) {
    return this.requestBitmap('img/titles2/', filename, hue, true);
};

ImageManager.requestBitmap = function(folder, filename, hue, smooth) {
    var path = folder + encodeURIComponent(filename) + '.png';
    var bitmap = ImageManager.requestNormalBitmap(path, hue || 0);
    bitmap.smooth = smooth;
    return bitmap;
};

ImageManager.requestNormalBitmap = function(path, hue){
    var key = this._generateCacheKey(path, hue);
    var bitmap = this._imageCache.get(key);
    if(!bitmap){
        bitmap = Bitmap.request(path);
        bitmap.addLoadListener(function(){
            bitmap.rotateHue(hue);
        });
        this._imageCache.add(key, bitmap);
        this._requestQueue.enqueue(key, bitmap);
    }else{
        this._requestQueue.raisePriority(key);
    }

    return bitmap;
};

ImageManager.update = function(){
    this._requestQueue.update();
};

ImageManager.clearRequest = function(){
    this._requestQueue.clear();
};
<|endoftext|>


<commit_before>document.addEventListener("DOMContentLoaded", function() {
  var PRIORITY_STATES = [ 'LA' ];

  var STATES = {
    "Alabama": "AL",
    "Alaska": "AK",
    "American Samoa": "AS",
    "Arizona": "AZ",
    "Arkansas": "AR",
    "California": "CA",
    "Colorado": "CO",
    "Connecticut": "CT",
    "Delaware": "DE",
    "District Of Columbia": "DC",
    "Federated States Of Micronesia": "FM",
    "Florida": "FL",
    "Georgia": "GA",
    "Guam": "GU",
    "Hawaii": "HI",
    "Idaho": "ID",
    "Illinois": "IL",
    "Indiana": "IN",
    "Iowa": "IA",
    "Kansas": "KS",
    "Kentucky": "KY",
    "Louisiana": "LA",
    "Maine": "ME",
    "Marshall Islands": "MH",
    "Maryland": "MD",
    "Massachusetts": "MA",
    "Michigan": "MI",
    "Minnesota": "MN",
    "Mississippi": "MS",
    "Missouri": "MO",
    "Montana": "MT",
    "Nebraska": "NE",
    "Nevada": "NV",
    "New Hampshire": "NH",
    "New Jersey": "NJ",
    "New Mexico": "NM",
    "New York": "NY",
    "North Carolina": "NC",
    "North Dakota": "ND",
    "Northern Mariana Islands": "MP",
    "Ohio": "OH",
    "Oklahoma": "OK",
    "Oregon": "OR",
    "Palau": "PW",
    "Pennsylvania": "PA",
    "Puerto Rico": "PR",
    "Rhode Island": "RI",
    "South Carolina": "SC",
    "South Dakota": "SD",
    "Tennessee": "TN",
    "Texas": "TX",
    "Utah": "UT",
    "Vermont": "VT",
    "Virgin Islands": "VI",
    "Virginia": "VA",
    "Washington": "WA",
    "West Virginia": "WV",
    "Wisconsin": "WI",
    "Wyoming": "WY"
  };

  var formatNumber = function(x) {
    return x ? x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
  };

  var pluralize = function(number, singular, plural=null){
    if (parseFloat(number) === 1.0) {
      return number + ' ' + singular;
    }
    else {
      if (!plural) {
        plural = singular + 's';
      }

      return formatNumber(number) + ' ' + plural;
    }
  };

  var shuffle = function(array) {
    for (var i = array.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  };

  // send event to Google Analytics
  var trackEvent = function(category, action, label, value) {
    if (!window.ga) return

    if (typeof label === undefined) {
      label = null;
    }

    if (typeof value === undefined) {
      value = null;
    }

    window.ga('send', 'event', category, action, label, value);
  };

  Vue.filter('formatNumber', formatNumber);
  Vue.filter('pluralize', pluralize);

  var app = new Vue({
    el: '#app',

    data: function() {
      return {
        states: STATES,
        selectedState: null,
        isLoaded: false,
        businesses: [],
        tweetCount: 0,
        name: null,
        email: null,
        zipCode: null,
        phone: null,
        hasLargeAudience: false,
        actionComment: null,
        isLoading: false,
        formMessage: null,
        modalVisible: false
      }
    },

    // watch: {
    //   tweetCount: function(val) {
    //     if (val === 4) {
    //       this.showModal()
    //     }
    //   }
    // },

    computed: {
      localBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return b.state === self.selectedState;
        }).slice(0, 20);
      },

      priorityBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return PRIORITY_STATES.indexOf(b.state) !== -1;
        }).slice(0, 20);
      },

      uniqueishBusinesses: function() {
        var uids = [];
        var businesses = [];

        for (var i = 0; i < this.businesses.length; i++) {
          var biz = this.businesses[i];
          var uid = biz.twitter + biz.state;

          if (uids.indexOf(uid) === -1) {
            uids.push(uid);
            businesses.push(biz);
          }
        }

        return businesses;
      }
    },

    created: function() {
      this.geocodeSelectedState();
      this.fetchBusinesses();
    },

    methods: {
      geocodeSelectedState: function() {
        var self = this;
        this.$http.get('https://fftf-geocoder.herokuapp.com').then(function(response){
          if (response.ok) {
            var geo = response.body;

            if (
              geo.country.iso_code === 'US' &&
              geo.subdivisions &&
              geo.subdivisions[0] &&
              geo.subdivisions[0].names &&
              geo.subdivisions[0].names.en
            ) {
              self.selectedState = geo.subdivisions[0].iso_code;
            }
          }
        });
      },

      fetchBusinesses: function() {
        var self = this;
        this.$http.get('https://data.battleforthenet.com/businesses.json').then(function(response){
          if (response.ok) {
            self.businesses = response.body;
            shuffle(self.businesses);
            self.isLoaded = true;
          }
        });
      },

      showModal: function() {
        this.modalVisible = true;
        document.querySelector('body').classList.add('modal-open');
      },

      hideModal: function() {
        this.modalVisible = false;
        document.querySelector('body').classList.remove('modal-open');
      },

      getMetaContent: function(name) {
        var el = document.querySelector('meta[name="' + name + '"]') || document.querySelector('meta[property="' + name + '"]');

        if (el) {
          return el.getAttribute('content');
        }

        return null;
      },

      openPopup: function(url, title='popup', w=600, h=500) {
        // Fixes dual-screen position
        var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
        var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;

        var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
        var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;

        var left = ((width / 2) - (w / 2)) + dualScreenLeft;
        var top = ((height / 2) - (h / 2)) + dualScreenTop;
        var newWindow = window.open(url, title, 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);

        // Puts focus on the newWindow
        if (window.focus) {
          newWindow.focus();
        }
      },

      shareOnFacebook: function() {
        var url = this.getMetaContent('og:url');
        this.openPopup('https://www.facebook.com/sharer.php?u=' + encodeURIComponent(url), 'facebook');
      },

      shareOnTwitter: function() {
        var tweetText = this.getMetaContent('twitter:description') + ' ' + this.getMetaContent('twitter:url');
        this.openPopup('https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText), 'twitter');
      },

      submitForm: function() {
        var self = this;
        self.isLoading = true;
        self.$http.post('https://queue.fightforthefuture.org/action', {
          member: {
            first_name: self.name,
            email: self.email,
            postcode: self.zipCode,
            phone_number: self.phone,
            country: 'US'
          },
          hp_enabled: 'true',
          guard: '',
          contact_congress: 0,
          org: 'fftf',
          an_tags: "[\"net-neutrality\"]",
          an_petition_id: '11f84b38-e65b-4259-b0ae-e879a4044ca9',
          volunteer: self.hasLargeAudience,
          action_comment: self.actionComment
        }, { emulateJSON: true })
        .then(function(response){
          self.isLoading = false;

          if (response.ok) {
            self.resetForm();
            self.showModal();
            trackEvent('signup', 'success', 'Sign Up', 'Step 4');
          }
          else {
            self.formMessage = "That didn't work for some reason :(";
          }
        })
        .catch(function(error){
          self.isLoading = false;
          self.formMessage = "That didn't work for some reason :(";
        })
      },

      resetForm() {
        this.phone = null;
        this.name = null;
        this.email = null;
        this.zipCode = null;
        this.hasLargeAudience = false;
        this.actionComment = null;
        this.formMessage = null;
      },

      openSpreadsheet() {
        window.open('https://docs.google.com/spreadsheets/d/12MkAWz8VGyIIiCgVdGXWfLKROBegZNew8-xKyPVv6vU/edit#gid=0', '_blank');
        trackEvent('button', 'click', 'Get Started', 'Step 5');
      }
    }
  });

  Vue.component('business-card', {
    template: '#business-card-template',

    props: {
      business: {
        type: Object
      },
      national: {
        type: Boolean,
        default: false
      }
    },

    computed: {
      tweetURL: function() {
        var tweetText = '';

        if (this.national) {
          tweetText = ".@" + this.business.twitter + " members of Congress REALLY listen to businesses. Can you sign on to this open letter supporting #NetNeutrality so lawmakers know how important this is to companies across the country?";
        }
        else {
          tweetText = ".@" + this.business.twitter + " your members of Congress aren't all supporting #NetNeutrality, and will soon face a CRA vote to overturn the FCC’s repeal.\n\nIt’s crucial that all businesses in " + this.$parent.selectedState + " take a stand. Can you sign on to this letter?";
        }

        return 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText) + '&url=' + encodeURIComponent('https://www.businessesfornetneutrality.com');
      },

      locationString: function() {
        if (this.business.city && this.business.state) {
          return [this.business.city, this.business.state].join(', ');
        }
        else if (this.business.city) {
          return this.business.city;
        }
        else if (this.business.state) {
          return this.business.state;
        }
      }
    },

    methods: {
      openTweetURL: function() {
        this.$parent.tweetCount += 1;
        window.open(this.tweetURL, '_blank');

        var stepNumber = this.national ? 3 : 1;
        trackEvent('business', 'tweet', this.business.twitter, 'Step ' + stepNumber);
      }
    }
  });
});<commit_msg>Remove default argument in business JS<commit_after>document.addEventListener("DOMContentLoaded", function() {
  var PRIORITY_STATES = [ 'LA' ];

  var STATES = {
    "Alabama": "AL",
    "Alaska": "AK",
    "American Samoa": "AS",
    "Arizona": "AZ",
    "Arkansas": "AR",
    "California": "CA",
    "Colorado": "CO",
    "Connecticut": "CT",
    "Delaware": "DE",
    "District Of Columbia": "DC",
    "Federated States Of Micronesia": "FM",
    "Florida": "FL",
    "Georgia": "GA",
    "Guam": "GU",
    "Hawaii": "HI",
    "Idaho": "ID",
    "Illinois": "IL",
    "Indiana": "IN",
    "Iowa": "IA",
    "Kansas": "KS",
    "Kentucky": "KY",
    "Louisiana": "LA",
    "Maine": "ME",
    "Marshall Islands": "MH",
    "Maryland": "MD",
    "Massachusetts": "MA",
    "Michigan": "MI",
    "Minnesota": "MN",
    "Mississippi": "MS",
    "Missouri": "MO",
    "Montana": "MT",
    "Nebraska": "NE",
    "Nevada": "NV",
    "New Hampshire": "NH",
    "New Jersey": "NJ",
    "New Mexico": "NM",
    "New York": "NY",
    "North Carolina": "NC",
    "North Dakota": "ND",
    "Northern Mariana Islands": "MP",
    "Ohio": "OH",
    "Oklahoma": "OK",
    "Oregon": "OR",
    "Palau": "PW",
    "Pennsylvania": "PA",
    "Puerto Rico": "PR",
    "Rhode Island": "RI",
    "South Carolina": "SC",
    "South Dakota": "SD",
    "Tennessee": "TN",
    "Texas": "TX",
    "Utah": "UT",
    "Vermont": "VT",
    "Virgin Islands": "VI",
    "Virginia": "VA",
    "Washington": "WA",
    "West Virginia": "WV",
    "Wisconsin": "WI",
    "Wyoming": "WY"
  };

  var formatNumber = function(x) {
    return x ? x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
  };

  var pluralize = function(number, singular, plural){
    if (parseFloat(number) === 1.0) {
      return number + ' ' + singular;
    }
    else {
      if (!plural) {
        plural = singular + 's';
      }

      return formatNumber(number) + ' ' + plural;
    }
  };

  var shuffle = function(array) {
    for (var i = array.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  };

  // send event to Google Analytics
  var trackEvent = function(category, action, label, value) {
    if (!window.ga) return

    if (typeof label === undefined) {
      label = null;
    }

    if (typeof value === undefined) {
      value = null;
    }

    window.ga('send', 'event', category, action, label, value);
  };

  Vue.filter('formatNumber', formatNumber);
  Vue.filter('pluralize', pluralize);

  var app = new Vue({
    el: '#app',

    data: function() {
      return {
        states: STATES,
        selectedState: null,
        isLoaded: false,
        businesses: [],
        tweetCount: 0,
        name: null,
        email: null,
        zipCode: null,
        phone: null,
        hasLargeAudience: false,
        actionComment: null,
        isLoading: false,
        formMessage: null,
        modalVisible: false
      }
    },

    // watch: {
    //   tweetCount: function(val) {
    //     if (val === 4) {
    //       this.showModal()
    //     }
    //   }
    // },

    computed: {
      localBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return b.state === self.selectedState;
        }).slice(0, 20);
      },

      priorityBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return PRIORITY_STATES.indexOf(b.state) !== -1;
        }).slice(0, 20);
      },

      uniqueishBusinesses: function() {
        var uids = [];
        var businesses = [];

        for (var i = 0; i < this.businesses.length; i++) {
          var biz = this.businesses[i];
          var uid = biz.twitter + biz.state;

          if (uids.indexOf(uid) === -1) {
            uids.push(uid);
            businesses.push(biz);
          }
        }

        return businesses;
      }
    },

    created: function() {
      this.geocodeSelectedState();
      this.fetchBusinesses();
    },

    methods: {
      geocodeSelectedState: function() {
        var self = this;
        this.$http.get('https://fftf-geocoder.herokuapp.com').then(function(response){
          if (response.ok) {
            var geo = response.body;

            if (
              geo.country.iso_code === 'US' &&
              geo.subdivisions &&
              geo.subdivisions[0] &&
              geo.subdivisions[0].names &&
              geo.subdivisions[0].names.en
            ) {
              self.selectedState = geo.subdivisions[0].iso_code;
            }
          }
        });
      },

      fetchBusinesses: function() {
        var self = this;
        this.$http.get('https://data.battleforthenet.com/businesses.json').then(function(response){
          if (response.ok) {
            self.businesses = response.body;
            shuffle(self.businesses);
            self.isLoaded = true;
          }
        });
      },

      showModal: function() {
        this.modalVisible = true;
        document.querySelector('body').classList.add('modal-open');
      },

      hideModal: function() {
        this.modalVisible = false;
        document.querySelector('body').classList.remove('modal-open');
      },

      getMetaContent: function(name) {
        var el = document.querySelector('meta[name="' + name + '"]') || document.querySelector('meta[property="' + name + '"]');

        if (el) {
          return el.getAttribute('content');
        }

        return null;
      },

      openPopup: function(url, title='popup', w=600, h=500) {
        // Fixes dual-screen position
        var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
        var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;

        var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
        var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;

        var left = ((width / 2) - (w / 2)) + dualScreenLeft;
        var top = ((height / 2) - (h / 2)) + dualScreenTop;
        var newWindow = window.open(url, title, 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);

        // Puts focus on the newWindow
        if (window.focus) {
          newWindow.focus();
        }
      },

      shareOnFacebook: function() {
        var url = this.getMetaContent('og:url');
        this.openPopup('https://www.facebook.com/sharer.php?u=' + encodeURIComponent(url), 'facebook');
      },

      shareOnTwitter: function() {
        var tweetText = this.getMetaContent('twitter:description') + ' ' + this.getMetaContent('twitter:url');
        this.openPopup('https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText), 'twitter');
      },

      submitForm: function() {
        var self = this;
        self.isLoading = true;
        self.$http.post('https://queue.fightforthefuture.org/action', {
          member: {
            first_name: self.name,
            email: self.email,
            postcode: self.zipCode,
            phone_number: self.phone,
            country: 'US'
          },
          hp_enabled: 'true',
          guard: '',
          contact_congress: 0,
          org: 'fftf',
          an_tags: "[\"net-neutrality\"]",
          an_petition_id: '11f84b38-e65b-4259-b0ae-e879a4044ca9',
          volunteer: self.hasLargeAudience,
          action_comment: self.actionComment
        }, { emulateJSON: true })
        .then(function(response){
          self.isLoading = false;

          if (response.ok) {
            self.resetForm();
            self.showModal();
            trackEvent('signup', 'success', 'Sign Up', 'Step 4');
          }
          else {
            self.formMessage = "That didn't work for some reason :(";
          }
        })
        .catch(function(error){
          self.isLoading = false;
          self.formMessage = "That didn't work for some reason :(";
        })
      },

      resetForm() {
        this.phone = null;
        this.name = null;
        this.email = null;
        this.zipCode = null;
        this.hasLargeAudience = false;
        this.actionComment = null;
        this.formMessage = null;
      },

      openSpreadsheet() {
        window.open('https://docs.google.com/spreadsheets/d/12MkAWz8VGyIIiCgVdGXWfLKROBegZNew8-xKyPVv6vU/edit#gid=0', '_blank');
        trackEvent('button', 'click', 'Get Started', 'Step 5');
      }
    }
  });

  Vue.component('business-card', {
    template: '#business-card-template',

    props: {
      business: {
        type: Object
      },
      national: {
        type: Boolean,
        default: false
      }
    },

    computed: {
      tweetURL: function() {
        var tweetText = '';

        if (this.national) {
          tweetText = ".@" + this.business.twitter + " members of Congress REALLY listen to businesses. Can you sign on to this open letter supporting #NetNeutrality so lawmakers know how important this is to companies across the country?";
        }
        else {
          tweetText = ".@" + this.business.twitter + " your members of Congress aren't all supporting #NetNeutrality, and will soon face a CRA vote to overturn the FCC’s repeal.\n\nIt’s crucial that all businesses in " + this.$parent.selectedState + " take a stand. Can you sign on to this letter?";
        }

        return 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText) + '&url=' + encodeURIComponent('https://www.businessesfornetneutrality.com');
      },

      locationString: function() {
        if (this.business.city && this.business.state) {
          return [this.business.city, this.business.state].join(', ');
        }
        else if (this.business.city) {
          return this.business.city;
        }
        else if (this.business.state) {
          return this.business.state;
        }
      }
    },

    methods: {
      openTweetURL: function() {
        this.$parent.tweetCount += 1;
        window.open(this.tweetURL, '_blank');

        var stepNumber = this.national ? 3 : 1;
        trackEvent('business', 'tweet', this.business.twitter, 'Step ' + stepNumber);
      }
    }
  });
});<|endoftext|>