<commit_before>// All except the last two will parse
var remarks = [
  '<Agent> <Verb> <Object> (result) {context} [attachments]',
  '<Agent> <Verb> <Object> (result) {context}',
  '<Agent> <Verb> <Object> (result)',
  '<Agent> <Verb> <Object>',
  '<Agent> <Verb> <Object> {context} [attachments]',
  '<Agent> <Verb> <Object> (result) [attachments]',
  '<Agent> <Verb> <Object> [attachments]',
  '<Agent> <Verb> <Object> {context}',
  '<tyler@example.com> <passed> <assessment1> ( (2, 0, 3) (2 minutes) ) { {chapter 1} {page 5, science 101, science} }',
  '<tyler@example.com> <passed> <assessment1> ( (2, 0, 3) ("PT2M") )',
  '<tyler@example.com> <passed> < dsaasghasdfasdfas',
  '<tyler@example.com> <3@)$@ _@#_!!dsaasghasdfasdfas'
];

/* Covert a Remark String into an array
 * TODO:
 * - match reserved verbs
 * - throw out invalid matches
 */
function remarkToArray(remark) {
  var re = /(<.+>) (<.+>) (<.+>)(\s?(\(.+\)))?(\s?(\{.+\}))?(\s?(\[.+\]))?/;
  var raw = remark.match(re);
  if (raw) {
    var arr = [];
    var i=0;
    //console.log(raw);
    raw.forEach(function(val) {
      // throw out "duplicates" created by the regex groupings
      if (i != 0 && i != 4 && i != 6 && i != 8) {
        // throw out empty groups
        if (val != undefined)
          arr.push(val);
      }
      i++;
    });
    console.log(arr);
    return arr;
  }
}

remarks.forEach(function(remark) {
  remarkToArray(remark);
});

// Merge two JSON object
function mergeJSON(obj1, obj2, result) {
  result = {};
  for(var key in obj1) result[key]=obj1[key];
  for(var key in obj2) result[key]=obj2[key];
  return result;
}

function remarkArrayToStatement(array) {
  //console.log(array);
  if (array.length > 3) {
    var avo = array.slice(0,3);
    var ex  = array.slice(3);
  } else {
    var avo = array;
  }
  var actor = avo[0].slice(1).slice(0,-1);
  var verb = avo[1].slice(1).slice(0,-1);
  var object = avo[2].slice(1).slice(0,-1);
  var stmt = {
    'actor': {
      'mbox': 'mailto:' + actor,
      'objectType': 'Agent'
    },
    'verb': {
      'display': {
        'en-US': verb
      },
      'id': 'http://adlnet.gov/expapi/verbs/' + verb
    },
    'object': {
      'id': 'http://example.com/' + object,
      'objectType': 'Object'
    }
  };
  // If more than an Actor, Verb and Object exist
  if (ex) {
    var stmt_ex = {};
    ex.forEach(function(o) {
      var type = o.substring(0,1);
      switch (type) {
        // result
        case "(":
          // turn it into an array of arrays
          arr = JSON.parse(o.replace(/\(/g,"[").replace(/\)/g,"]").replace(/\] \[/,"],[").trim());
          var scores = arr[0];
          var duration = arr[1];
          stmt_ex = {
            'result': {
              'score': {
                'raw': scores[0],
                'min': scores[1],
                'max': scores[2]
              },
              'duration': duration[0]
            }
          };
        break;
        // context
        case "{":
        break;
        // attachments
        case "[":
        break;
        default:
      }
      stmt = mergeJSON(stmt, stmt_ex);
    });
    return stmt;
  }
  return stmt;
}

var array = remarkToArray(remarks[9]);
var stmt = remarkArrayToStatement(array);
console.log(stmt);
<commit_msg>Must manually use ISO 8601 duration for now<commit_after>// All except the last two will parse
var remarks = [
  '<Agent> <Verb> <Object> (result) {context} [attachments]',
  '<Agent> <Verb> <Object> (result) {context}',
  '<Agent> <Verb> <Object> (result)',
  '<Agent> <Verb> <Object>',
  '<Agent> <Verb> <Object> {context} [attachments]',
  '<Agent> <Verb> <Object> (result) [attachments]',
  '<Agent> <Verb> <Object> [attachments]',
  '<Agent> <Verb> <Object> {context}',
  '<tyler@example.com> <passed> <assessment1> ( (2, 0, 3) ("PT2M") ) { {chapter 1} {page 5, science 101, science} }',
  '<tyler@example.com> <passed> <assessment1> ( (2, 0, 3) ("PT2M") )',
  '<tyler@example.com> <passed> < dsaasghasdfasdfas',
  '<tyler@example.com> <3@)$@ _@#_!!dsaasghasdfasdfas'
];

/* Covert a Remark String into an array
 * TODO:
 * - match reserved verbs
 * - throw out invalid matches
 */
function remarkToArray(remark) {
  var re = /(<.+>) (<.+>) (<.+>)(\s?(\(.+\)))?(\s?(\{.+\}))?(\s?(\[.+\]))?/;
  var raw = remark.match(re);
  if (raw) {
    var arr = [];
    var i=0;
    //console.log(raw);
    raw.forEach(function(val) {
      // throw out "duplicates" created by the regex groupings
      if (i != 0 && i != 4 && i != 6 && i != 8) {
        // throw out empty groups
        if (val != undefined)
          arr.push(val);
      }
      i++;
    });
    console.log(arr);
    return arr;
  }
}

remarks.forEach(function(remark) {
  remarkToArray(remark);
});

// Merge two JSON object
function mergeJSON(obj1, obj2, result) {
  result = {};
  for(var key in obj1) result[key]=obj1[key];
  for(var key in obj2) result[key]=obj2[key];
  return result;
}

function remarkArrayToStatement(array) {
  //console.log(array);
  if (array.length > 3) {
    var avo = array.slice(0,3);
    var ex  = array.slice(3);
  } else {
    var avo = array;
  }
  var actor = avo[0].slice(1).slice(0,-1);
  var verb = avo[1].slice(1).slice(0,-1);
  var object = avo[2].slice(1).slice(0,-1);
  var stmt = {
    'actor': {
      'mbox': 'mailto:' + actor,
      'objectType': 'Agent'
    },
    'verb': {
      'display': {
        'en-US': verb
      },
      'id': 'http://adlnet.gov/expapi/verbs/' + verb
    },
    'object': {
      'id': 'http://example.com/' + object,
      'objectType': 'Object'
    }
  };
  // If more than an Actor, Verb and Object exist
  if (ex) {
    var stmt_ex = {};
    ex.forEach(function(o) {
      var type = o.substring(0,1);
      switch (type) {
        // result
        case "(":
          // turn it into an array of arrays
          arr = JSON.parse(o.replace(/\(/g,"[").replace(/\)/g,"]").replace(/\] \[/,"],[").trim());
          var scores = arr[0];
          var duration = arr[1];
          stmt_ex = {
            'result': {
              'score': {
                'raw': scores[0],
                'min': scores[1],
                'max': scores[2]
              },
              'duration': duration[0]
            }
          };
        break;
        // context
        case "{":
        break;
        // attachments
        case "[":
        break;
        default:
      }
      stmt = mergeJSON(stmt, stmt_ex);
    });
    return stmt;
  }
  return stmt;
}

var array = remarkToArray(remarks[9]);
var stmt = remarkArrayToStatement(array);
console.log(stmt);
<|endoftext|>


<commit_before>define(['jquery',
        'mustache',
        'text!geobricks_ui_processing/html/templates.html',
        'i18n!geobricks_ui_processing/nls/translate',
        'text!geobricks_ui_processing/config/modis.json',
        'sweet-alert',
        'bootstrap'], function ($, Mustache, templates, translate, modis_configuration) {

    'use strict';

    function PROCESSING() {

        this.CONFIG = {
            lang: 'en',
            target_dir: null,
            filenames: null,
            url_processing: 'http://localhost:5555/processing/process/'
        };

    }

    /**
     * This is the entry method to configure the module.
     *
     * @param config Custom configuration in JSON format to extend the default settings.
     */
    PROCESSING.prototype.init = function(config) {

        /* Extend default configuration. */
        this.CONFIG = $.extend(true, {}, this.CONFIG, config);

        /* Fix the language, if needed. */
        this.CONFIG.lang = this.CONFIG.lang != null ? this.CONFIG.lang : 'en';

        /* Cast JSON files, if needed. */
        this.CONFIG.modis_configuration = $.parseJSON(modis_configuration);

        /* Source paths for the first step of the processing. */
        var source_paths = [];
        for (var i = 0 ; i < this.CONFIG.filenames.length ; i++)
            source_paths.push(this.CONFIG.target_dir + '/' + this.CONFIG.filenames[i]);

        console.debug('PROCESS START:');
        for (i = 0 ; i < source_paths.length ; i++)
            console.debug(source_paths[i]);
        console.debug();

        /* Process the first object. */
        this.process_step(source_paths, this.CONFIG.modis_configuration[Object.keys(this.CONFIG.modis_configuration)[0]], 1);

    };

    PROCESSING.prototype.process_step = function(source_paths, process_object, next_index) {

        /* This. */
        var _this = this;

        /* Edit the process object. */
        for (var i = 0 ; i < process_object.length ; i++) {
            process_object[i].source_path = source_paths;
            process_object[i].output_path = this.CONFIG.target_dir + '/extract_bands';
        }

        $.ajax({

            url: this.CONFIG.url_processing,
            type: 'POST',
            dataType: 'json',
            data: JSON.stringify(process_object),
            contentType: 'application/json',

            success: function (response) {

                /* Cast the response to JSON, if needed. */
                var json = response;
                if (typeof json == 'string')
                    json = $.parseJSON(response);

                /* Process next step, if any. */
                try {
                    var next_p_o = _this.CONFIG.modis_configuration[Object.keys(_this.CONFIG.modis_configuration)[next_index]];
                    _this.process_step(json, next_p_o, 1 + parseInt(next_index));
                } catch(e) {
                    sweetAlert({
                        title: translate.info,
                        text: translate.processing_complete,
                        type: 'info',
                        confirmButtonColor: '#379BCE'
                    });
                }

            },

            error: function(a, b, c) {
                sweetAlert({
                    title: translate.error,
                    text: e,
                    type: 'error',
                    confirmButtonColor: '#379BCE'
                });
            }

        });

    };

    return new PROCESSING();

});
<commit_msg>NASA service unavailable.<commit_after>define(['jquery',
        'mustache',
        'text!geobricks_ui_processing/html/templates.html',
        'i18n!geobricks_ui_processing/nls/translate',
        'text!geobricks_ui_processing/config/modis.json',
        'sweet-alert',
        'bootstrap'], function ($, Mustache, templates, translate, modis_configuration) {

    'use strict';

    function PROCESSING() {

        this.CONFIG = {
            lang: 'en',
            target_dir: null,
            filenames: null,
            url_processing: 'http://localhost:5555/processing/process/'
        };

    }

    /**
     * This is the entry method to configure the module.
     *
     * @param config Custom configuration in JSON format to extend the default settings.
     */
    PROCESSING.prototype.init = function(config) {

        /* Extend default configuration. */
        this.CONFIG = $.extend(true, {}, this.CONFIG, config);

        /* Fix the language, if needed. */
        this.CONFIG.lang = this.CONFIG.lang != null ? this.CONFIG.lang : 'en';

        /* Cast JSON files, if needed. */
        this.CONFIG.modis_configuration = $.parseJSON(modis_configuration);

        /* Source paths for the first step of the processing. */
        var source_paths = [];
        for (var i = 0 ; i < this.CONFIG.filenames.length ; i++)
            source_paths.push(this.CONFIG.target_dir + '/' + this.CONFIG.filenames[i]);

        console.debug('PROCESS START:');
        for (i = 0 ; i < source_paths.length ; i++)
            console.debug(source_paths[i]);
        console.debug();

        /* Process the first object. */
        this.process_step(source_paths, this.CONFIG.modis_configuration[Object.keys(this.CONFIG.modis_configuration)[0]], 1);

    };

    PROCESSING.prototype.process_step = function(source_paths, process_object, next_index) {

        /* This. */
        var _this = this;

        /* Edit the process object. */
        for (var i = 0 ; i < process_object.length ; i++) {
            process_object[i].source_path = source_paths;
            process_object[i].output_path = this.CONFIG.target_dir + '/extract_bands';
        }

        $.ajax({

            url: this.CONFIG.url_processing,
            type: 'POST',
            dataType: 'json',
            data: JSON.stringify(process_object),
            contentType: 'application/json',

            success: function (response) {

                /* Cast the response to JSON, if needed. */
                var json = response;
                if (typeof json == 'string')
                    json = $.parseJSON(response);

                /* Process next step, if any. */
                try {
                    var next_p_o = _this.CONFIG.modis_configuration[Object.keys(_this.CONFIG.modis_configuration)[next_index]];
                    _this.process_step(json, next_p_o, 1 + parseInt(next_index));
                } catch(e) {
                    sweetAlert({
                        title: translate.info,
                        text: translate.processing_complete,
                        type: 'info',
                        confirmButtonColor: '#379BCE'
                    });
                }

            },

            error: function(a, b, c) {
                sweetAlert({
                    title: translate.error,
                    text: e,
                    type: 'error',
                    confirmButtonColor: '#379BCE'
                });
            }

        });

    };

    return PROCESSING;

});
<|endoftext|>


<commit_before>var Spytext = function() {
	var that = this;
	this.fields = [];
	this.currentField = null;
	this.buttons = [];
	_.each(this.events.doc, function(func, name) {
		document.bind(name, func, that);
	});
	this.mousedown = false;
};
Spytext.prototype = {
	setCurrentField: function(field) {
		if(this.fields.indexOf(field) > -1) {
			this.currentField = field;
			// TODO activate buttons
		} else {
			this.unsetCurrentField();
		}
	},
	unsetCurrentField: function() {
		this.currentField = null;
		// TODO deactivate buttons
	},
	events: {
		doc: {
			mouseup: function(e) {
				if(this.mousedown && this.currentField) {
					this.currentField.snapback.register();
					this.currentField.snapback.setSelection();
				}
				this.mousedown = false;
			}
		}
	},
	actions: {
		align: function(options) {
			var points = S.points.both(this.currentField.element);
			S.set(points);

			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks.each(function() {
				if(!this.matches('ul, ol')) this.css('text-align', options.alignment);
			});
		},
		block: function(options) {
			var wrapper = O('<' + options.tag + '></' + options.tag + '>');
			var points = S.points.both(this.currentField.element);
			S.set(points);
			var sel = S.s();

			// TODO split list
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks.each(function() {
				var that = this;
				var tmp;
				if(this.nodeName === 'UL' || this.nodeName === 'OL') {
					var listItems = this.M('li');
					listItems.each(function() {
						S.set(points);
						if(sel.containsNode(this, true)) {
							tmp = wrapper.clone();
							if(this.previousSibling) that.after(tmp);
							else that.before(tmp);
							while(this.firstChild) {
								tmp.append(this.firstChild);
							}
							this.remove();
						}
					});
					if(!this.firstChild) this.remove();
				} else {
					tmp = wrapper.clone();
					this.before(tmp);
					while(this.firstChild) {
						tmp.append(this.firstChild);
					}
					this.remove();
				}
			});
			S.set(points);
		},
		list: function(options){
			var tags = {
				ordered: 'OL',
				unordered: 'UL'
			};
			var tagName = tags[options.type];
			var list = O('<' + tagName + '></' + tagName + '>');
			var selection = S.save(this.currentField.element);
			selection.load();
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks[0].before(list);
			containedBlocks.each(function(){
				if(this.nodeName === 'UL' || this.nodeName === 'OL') {
					if(containedBlocks.length === 1) {
						if(this.tagName !== tagName) {
							var containedLi = S.nodes.elements(this, true, null, true);
							if(this.firstChild === containedLi[0]) {
								this.before(list);
								containedLi.each(function() {
									list.append(this);
								});
								if(!this.firstChild) this.remove();
							} else if (this.lastChild === containedLi[containedLi.length - 1]) {
								this.after(list);
								while(this.lastChild === containedLi[containedLi.length - 1]) {
									containedLi.each(function() {
										list.append(this);
									});
								}
							} else {
								var bottomList = O('<' + this.tagName + '><' + this.tagName + '/>');
								this.after(bottomList);
								while(this.lastChild !== containedLi[containedLi.length - 1]) {
									bottomList.prepend(this.lastChild);
								}
								this.after(list);
								containedLi.each(function() {
									list.append(this);
								});
							}
						}
					} else {
						while(this.firstChild) {
							list.append(this.firstChild);
						}
						this.remove();
					}
				} else {
					var listItem = O('

');
					list.append(listItem);
					while(this.firstChild) {
						listItem.append(this.firstChild);
					}
					list.append(listItem);
					this.remove();
				}
			});
			selection.load();
		},
		indent: function(){
		},
		format: function(options){
			var tags = {
				underline: 'U',
				bold: 'B'
			};
			var wrapper = options.container ? O(options.container) : O('<' + tags[options.command] + '></' + tags[options.command] + '>');
			var points = S.points.both(this.currentField.element);
			S.set(points);
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			if(S.isCollapsed()){
				var closest = rng.startContainer.closest(wrapper.tagName);
				while(closest) {
					closest.unwrap();
					closest = closest.closest(wrapper.tagName);
				}
			} else {
				var textNodes = S.nodes.textNodes(this.currentField.element).toArray();
				var rng = S.range();
				if(rng.endOffset < rng.endContainer.textContent.length) {
					rng.endContainer.splitText(rng.endOffset);
				}
				if(rng.startOffset > 0) {
					textNodes = textNodes.slice(1);
					textNodes.unshift(rng.startContainer.splitText(rng.startOffset));
				}
				textNodes = M(textNodes);
				textNodes.wrap(wrapper);

			}
			containedBlocks.tidy(wrapper.tagName);
			S.set(points);
		},
		link: function (attribute) {
			var sel = window.getSelection();
			var node = sel.focusNode.parentNode;
			if (node.tagName.toLowerCase() !== 'a') {
				node = sel.anchorNode.parentNode;
				if (node.tagName.toLowerCase() !== 'a') {
					node = null;
				}
			}

			var href = 'http://';
			if (node) {
				var range = document.createRange();
				range.selectNodeContents(node);
				href = node.attributeibutes.href.value;
				sel.removeAllRanges();
				sel.addRange(range);
			}
			var result = prompt('Link address:', href);

			if (result !== '') {
				document.execCommand('createLink', null, result);
			} else {
				document.execCommand('unlink');
			}
		},
		paste: function(options) {
		},
		type: {}
	},
	addButton: function(element, config) {
		var field = new SpytextButton(element, config, this);
		this.buttons.push(field);
		return field;
	},
	addField: function(element, config, snapback) {
		var button = new SpytextField(this, element, config, snapback);
		this.fields.push(button);
		return button;
	},
	execute: function(action, options) {
		var snapback = this.currentField.snapback;
		snapback.register();
		snapback.setSelection();
		this.actions[action].call(this, options);
		setTimeout(function() {
			snapback.register();
		}, 100);
	}
};
var SpytextButton = function(element, config, spytext) {
	this.spytext = spytext;
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.element = element;
	this.element.bind('click', this.events.click, this);
};
SpytextButton.prototype = {
	events: {
		click: function() {
			this.spytext.execute(this.config.action, this.config.options);
		}
	},
	presets: {
		alignLeft: { title: 'Align Left', action: 'align', options: { alignment: 'left' }},
		alignRight: { title: 'Align Right', action: 'align', options: { alignment: 'right' }},
		alignCenter: { title: 'Align Center', action: 'align', options: { alignment: 'center' }},
		alignJustify: { title: 'Align Justify', action: 'align', options: { alignment: 'justify' }},
		bold: { title: 'Bold', action: 'format', options: { command: 'bold' }},
		strikeThrough: { title: 'Strike Through', action: 'format', options: { command: 'strikeThrough' }},
		underline: { title: 'Underline', action: 'format', options: { command: 'italic' }},
		italic: { title: 'Italic', action: 'format', options: { command: 'bold' }},
		removeFormat: { action: 'format', options: { command: 'removeFormat' }},
		typeHeading1: { title: 'Heading 1', action: 'block', options: { tag: 'H1' }},
		typeHeading2: { title: 'Heading 1', action: 'block', options: { tag: 'H2' }},
		typeHeading3: { title: 'Heading 1', action: 'block', options: { tag: 'H3' }},
		typeHeading4: { title: 'Heading 1', action: 'block', options: { tag: 'H4' }},
		typeHeading5: { title: 'Heading 1', action: 'block', options: { tag: 'H5' }},
		typeHeading6: { title: 'Heading 1', action: 'block', options: { tag: 'H6' }},
		typeParagraph: { title: 'Heading 1', action: 'block', options: { tag: 'P' }},
		orderedList: { title: 'Ordered List', action: 'list', options: { type: 'ordered' }},
		unorderedList: { title: 'Unordered List', action: 'list', options: { type: 'unordered' }},
	}
};
var SpytextToolbar = function(element, config) {
	var that = this;
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.element = element;
	_.each(this.events, function(func, name) {
		that.element.bind(name, func, that);
	});
};
SpytextToolbar.prototype = {
	presets: {
		standard: {
			buttonGroups: [
				{ name: 'block', buttons: ['typeHeading1', 'typeHeading2', 'typeHeading3', 'typeHeading4', 'typeHeading5', 'typeHeading6', 'typeParagraph']},
				{ name: 'format', buttons: ['bold', 'underline', 'strikeThrough', 'removeFormat']},
				{ name: 'align', buttons: ['alignLeft', 'alignCenter', 'alignRight', 'alignJustify']},
				{ name: 'list', buttons: ['unorderedList', 'orderedList']}
			]
		}
	},
	events: {
		mousedown: function(e) {
			// this is needed to prevent toolbar from stealing focus
			e.preventDefault();
		}
	}
};
var SpytextField = function(spytext, element, config, snapback) {
	var that = this;
	this.spytext = spytext;
	this.element = element;
	this.element.attr('contentEditable', 'true');
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.snapback = snapback;
	this.timeout = null;
	_.each(this.events, function(func, name) {
		element.bind(name, func, that);
	});
};
SpytextField.prototype = {
	activate: function() {
		this.spytext.setCurrentField(this);
	},
	deactivate: function() {
		this.spytext.unsetCurrentField();
	},
	presets: {
		full: {
			buttons: [ 'bold', 'italic', 'underline', 'strikeThrough', 'removeFormat' ]
		}
	},
	events: {
		focus: function () {
			//$scope.activateButtons(buttons);
			this.snapback.toggle();
			this.activate();
			//if(!mousedown) {
			//	updateSelection();
			//}
		},
		blur: function () {
			if(!this.mousedown) {
				this.snapback.register();
			}
			this.snapback.toggle();
			this.deactivate();
		},
		mousedown: function(e) {
			this.snapback.register();
			this.spytext.mousedown = true;
		},
		keyup: function(e) {
		},
		keydown: function(e) {
			if (e.ctrlKey) {
				clearTimeout(this.timeout);
				this.timeout = null;
				this.snapback.register();
				switch(e.keyCode) {
					case 66://b
					case 85://u
						e.preventDefault();
						var arr = [];
						arr[66] = 'bold';
						arr[85] = 'underline';
						this.spytext.execute('format', { command: arr[e.keyCode] }, this.element);
						break;
					case 89://y
						e.preventDefault();
						this.snapback.redo();
						break;
					case 90://z
						e.preventDefault();
						this.snapback.undo();
						break;
					case 65://a
						e.preventDefault();
						S.set({ start: { ref: this.element, offset: 0 }, end: { ref: this.element, offset: this.element.textContent.length } });
						break;
					case 84://t
						e.preventDefault();
						break;
					case 86://v
						// DO nothing, let paste event be handles
						break;
				}
			} else {
				switch(e.keyCode) {
					case 33:
					case 34:
					case 35:
					case 36:
					case 37:
					case 38:
					case 39:
					case 40:
						clearTimeout(this.timeout);
						this.timeout = null;
						this.snapback.register();
						break;
					default:
						var that = this;
						if(!this.timeout) {
							console.log('setting selection');
							that.snapback.setSelection();
						}
						clearTimeout(this.timeout);
						this.timeout = setTimeout(function() {
							console.log('timeout');
							that.snapback.register();
						}, 1000);
						break;
				}
			}
		},
		paste: function (e) {
			e.preventDefault();
			var str;
			if(e.clipboardData) {
				e.clipboardData.items[0].getAsString(function(str) {
					done(str);
				});
			} else {
				done(clipboardData.getData('Text'));
			}
			function done() {
				str = str.replace(/</g, '<').replace(/>/, '>').replace(/[\n\r]+/g, '

');
				this.spytext.execute('paste', { text: str });
			}
		}
	}
};
<commit_msg>Rebuilt selectron slightly. Undos seem to work perfectly now.<commit_after>var Spytext = function() {
	var that = this;
	this.fields = [];
	this.currentField = null;
	this.buttons = [];
	_.each(this.events.doc, function(func, name) {
		document.bind(name, func, that);
	});
	this.mousedown = false;
};
Spytext.prototype = {
	setCurrentField: function(field) {
		if(this.fields.indexOf(field) > -1) {
			this.currentField = field;
			// TODO activate buttons
		} else {
			this.unsetCurrentField();
		}
	},
	unsetCurrentField: function() {
		this.currentField = null;
		// TODO deactivate buttons
	},
	events: {
		doc: {
			mouseup: function(e) {
				if(this.mousedown && this.currentField) {
					this.currentField.snapback.register();
					this.currentField.snapback.setSelectron();
				}
				this.mousedown = false;
			}
		}
	},
	actions: {
		align: function(options) {
			var points = S.points.both(this.currentField.element);
			S.set(points);

			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks.each(function() {
				if(!this.matches('ul, ol')) this.css('text-align', options.alignment);
			});
		},
		block: function(options) {
			var wrapper = O('<' + options.tag + '></' + options.tag + '>');
			var points = S.points.both(this.currentField.element);
			S.set(points);
			var sel = S.s();

			// TODO split list
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks.each(function() {
				var that = this;
				var tmp;
				if(this.nodeName === 'UL' || this.nodeName === 'OL') {
					var listItems = this.M('li');
					listItems.each(function() {
						S.set(points);
						if(sel.containsNode(this, true)) {
							tmp = wrapper.clone();
							if(this.previousSibling) that.after(tmp);
							else that.before(tmp);
							while(this.firstChild) {
								tmp.append(this.firstChild);
							}
							this.remove();
						}
					});
					if(!this.firstChild) this.remove();
				} else {
					tmp = wrapper.clone();
					this.before(tmp);
					while(this.firstChild) {
						tmp.append(this.firstChild);
					}
					this.remove();
				}
			});
			S.set(points);
		},
		list: function(options){
			var tags = {
				ordered: 'OL',
				unordered: 'UL'
			};
			var tagName = tags[options.type];
			var list = O('<' + tagName + '></' + tagName + '>');
			var selection = S.save(this.currentField.element);
			selection.load();
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			containedBlocks[0].before(list);
			containedBlocks.each(function(){
				if(this.nodeName === 'UL' || this.nodeName === 'OL') {
					if(containedBlocks.length === 1) {
						if(this.tagName !== tagName) {
							var containedLi = S.nodes.elements(this, true, null, true);
							if(this.firstChild === containedLi[0]) {
								this.before(list);
								containedLi.each(function() {
									list.append(this);
								});
								if(!this.firstChild) this.remove();
							} else if (this.lastChild === containedLi[containedLi.length - 1]) {
								this.after(list);
								while(this.lastChild === containedLi[containedLi.length - 1]) {
									containedLi.each(function() {
										list.append(this);
									});
								}
							} else {
								var bottomList = O('<' + this.tagName + '><' + this.tagName + '/>');
								this.after(bottomList);
								while(this.lastChild !== containedLi[containedLi.length - 1]) {
									bottomList.prepend(this.lastChild);
								}
								this.after(list);
								containedLi.each(function() {
									list.append(this);
								});
							}
						}
					} else {
						while(this.firstChild) {
							list.append(this.firstChild);
						}
						this.remove();
					}
				} else {
					var listItem = O('

');
					list.append(listItem);
					while(this.firstChild) {
						listItem.append(this.firstChild);
					}
					list.append(listItem);
					this.remove();
				}
			});
			selection.load();
		},
		indent: function(){
		},
		format: function(options){
			var tags = {
				underline: 'U',
				bold: 'B'
			};
			var wrapper = options.container ? O(options.container) : O('<' + tags[options.command] + '></' + tags[options.command] + '>');
			var points = S.points.both(this.currentField.element);
			S.set(points);
			var containedBlocks = S.nodes.elements(this.currentField.element, true, null, true);
			if(S.isCollapsed()){
				var closest = rng.startContainer.closest(wrapper.tagName);
				while(closest) {
					closest.unwrap();
					closest = closest.closest(wrapper.tagName);
				}
			} else {
				var textNodes = S.nodes.textNodes(this.currentField.element).toArray();
				var rng = S.range();
				if(rng.endOffset < rng.endContainer.textContent.length) {
					rng.endContainer.splitText(rng.endOffset);
				}
				if(rng.startOffset > 0) {
					textNodes = textNodes.slice(1);
					textNodes.unshift(rng.startContainer.splitText(rng.startOffset));
				}
				textNodes = M(textNodes);
				textNodes.wrap(wrapper);

			}
			containedBlocks.tidy(wrapper.tagName);
			S.set(points);
		},
		link: function (attribute) {
			var sel = window.getSelection();
			var node = sel.focusNode.parentNode;
			if (node.tagName.toLowerCase() !== 'a') {
				node = sel.anchorNode.parentNode;
				if (node.tagName.toLowerCase() !== 'a') {
					node = null;
				}
			}

			var href = 'http://';
			if (node) {
				var range = document.createRange();
				range.selectNodeContents(node);
				href = node.attributeibutes.href.value;
				sel.removeAllRanges();
				sel.addRange(range);
			}
			var result = prompt('Link address:', href);

			if (result !== '') {
				document.execCommand('createLink', null, result);
			} else {
				document.execCommand('unlink');
			}
		},
		paste: function(options) {
		},
		type: {}
	},
	addButton: function(element, config) {
		var field = new SpytextButton(element, config, this);
		this.buttons.push(field);
		return field;
	},
	addField: function(element, config, snapback) {
		var button = new SpytextField(this, element, config, snapback);
		this.fields.push(button);
		return button;
	},
	execute: function(action, options) {
		var snapback = this.currentField.snapback;
		snapback.register();
		snapback.setSelectron();
		this.actions[action].call(this, options);
		setTimeout(function() {
			snapback.register();
		}, 100);
	}
};
var SpytextButton = function(element, config, spytext) {
	this.spytext = spytext;
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.element = element;
	this.element.bind('click', this.events.click, this);
};
SpytextButton.prototype = {
	events: {
		click: function() {
			this.spytext.execute(this.config.action, this.config.options);
		}
	},
	presets: {
		alignLeft: { title: 'Align Left', action: 'align', options: { alignment: 'left' }},
		alignRight: { title: 'Align Right', action: 'align', options: { alignment: 'right' }},
		alignCenter: { title: 'Align Center', action: 'align', options: { alignment: 'center' }},
		alignJustify: { title: 'Align Justify', action: 'align', options: { alignment: 'justify' }},
		bold: { title: 'Bold', action: 'format', options: { command: 'bold' }},
		strikeThrough: { title: 'Strike Through', action: 'format', options: { command: 'strikeThrough' }},
		underline: { title: 'Underline', action: 'format', options: { command: 'italic' }},
		italic: { title: 'Italic', action: 'format', options: { command: 'bold' }},
		removeFormat: { action: 'format', options: { command: 'removeFormat' }},
		typeHeading1: { title: 'Heading 1', action: 'block', options: { tag: 'H1' }},
		typeHeading2: { title: 'Heading 1', action: 'block', options: { tag: 'H2' }},
		typeHeading3: { title: 'Heading 1', action: 'block', options: { tag: 'H3' }},
		typeHeading4: { title: 'Heading 1', action: 'block', options: { tag: 'H4' }},
		typeHeading5: { title: 'Heading 1', action: 'block', options: { tag: 'H5' }},
		typeHeading6: { title: 'Heading 1', action: 'block', options: { tag: 'H6' }},
		typeParagraph: { title: 'Heading 1', action: 'block', options: { tag: 'P' }},
		orderedList: { title: 'Ordered List', action: 'list', options: { type: 'ordered' }},
		unorderedList: { title: 'Unordered List', action: 'list', options: { type: 'unordered' }},
	}
};
var SpytextToolbar = function(element, config) {
	var that = this;
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.element = element;
	_.each(this.events, function(func, name) {
		that.element.bind(name, func, that);
	});
};
SpytextToolbar.prototype = {
	presets: {
		standard: {
			buttonGroups: [
				{ name: 'block', buttons: ['typeHeading1', 'typeHeading2', 'typeHeading3', 'typeHeading4', 'typeHeading5', 'typeHeading6', 'typeParagraph']},
				{ name: 'format', buttons: ['bold', 'underline', 'strikeThrough', 'removeFormat']},
				{ name: 'align', buttons: ['alignLeft', 'alignCenter', 'alignRight', 'alignJustify']},
				{ name: 'list', buttons: ['unorderedList', 'orderedList']}
			]
		}
	},
	events: {
		mousedown: function(e) {
			// this is needed to prevent toolbar from stealing focus
			e.preventDefault();
		}
	}
};
var SpytextField = function(spytext, element, config, snapback) {
	var that = this;
	this.spytext = spytext;
	this.element = element;
	this.element.attr('contentEditable', 'true');
	this.config = typeof config === 'string' ? this.presets[config] : config;
	this.snapback = snapback;
	this.timeout = null;
	_.each(this.events, function(func, name) {
		element.bind(name, func, that);
	});
};
SpytextField.prototype = {
	activate: function() {
		this.spytext.setCurrentField(this);
	},
	deactivate: function() {
		this.spytext.unsetCurrentField();
	},
	presets: {
		full: {
			buttons: [ 'bold', 'italic', 'underline', 'strikeThrough', 'removeFormat' ]
		}
	},
	events: {
		focus: function () {
			this.snapback.enable();
			this.activate();
		},
		blur: function () {
			this.snapback.register();
			this.snapback.disable();
			this.deactivate();
		},
		mousedown: function(e) {
			this.spytext.mousedown = true;
		},
		keyup: function(e) {
		},
		keydown: function(e) {
			if (e.ctrlKey) {
				clearTimeout(this.timeout);
				this.timeout = null;
				this.snapback.register();
				switch(e.keyCode) {
					case 66://b
					case 85://u
						e.preventDefault();
						var arr = [];
						arr[66] = 'bold';
						arr[85] = 'underline';
						this.spytext.execute('format', { command: arr[e.keyCode] }, this.element);
						break;
					case 89://y
						e.preventDefault();
						this.snapback.redo();
						break;
					case 90://z
						e.preventDefault();
						this.snapback.undo();
						break;
					case 65://a
						e.preventDefault();
						S.set({ start: { ref: this.element, offset: 0 }, end: { ref: this.element, offset: this.element.textContent.length } });
						break;
					case 84://t
						e.preventDefault();
						break;
					case 86://v
						// DO nothing, let paste event be handles
						break;
				}
			} else {
				switch(e.keyCode) {
					case 33:
					case 34:
					case 35:
					case 36:
					case 37:
					case 38:
					case 39:
					case 40:
						clearTimeout(this.timeout);
						this.timeout = null;
						this.snapback.register();
						break;
					default:
						var that = this;
						clearTimeout(this.timeout);
						this.timeout = setTimeout(function() {
							that.timeout = null;
							that.snapback.register();
						}, 1000);
						break;
				}
			}
		},
		paste: function (e) {
			e.preventDefault();
			var str;
			if(e.clipboardData) {
				e.clipboardData.items[0].getAsString(function(str) {
					done(str);
				});
			} else {
				done(clipboardData.getData('Text'));
			}
			function done() {
				str = str.replace(/</g, '<').replace(/>/, '>').replace(/[\n\r]+/g, '

');
				this.spytext.execute('paste', { text: str });
			}
		}
	}
};
<|endoftext|>


<commit_before>/* eslint browser: true */
/* global chrome */
/* TODO this really wants to become a module when google fix the bug with them*/

const defaults = {
  // 'original' or 'new'
  focus: 'new',
  // boolean
  resizeOriginal: true,
  // boolean
  cloneOriginal: false,
  // clone-position-same', 'clone-position-horizontal', 'clone-position-vertical'
  clonePosition: 'clone-position-horizontal',
  // boolean
  copyFullscreen: true,
  // 'normal', 'popup'
  menuButtonType: 'normal',
  // these are all percentage 0.0 - 1.0
  originalWidth:  0.5,
  originalHeight: 1.0,
  originalLeft:   0.0,
  originalTop:    0.0,
  newWidth:       0.5,
  newHeight:      1.0,
  newLeft:        0.5,
  newTop:         0.0
};

// retrieve the localStorage key for a particular window property
// @windowId: 'original', 'new'
// @propertyKey: 'width', 'height', 'left', 'top'
function getStorageWindowPropKey(windowId, propKey) {
  return `${windowId}${propKey.slice(0).charAt().toUpperCase()}${propKey.slice(1)}`;
}

function validateOptions(options) {
  if (!Object.keys(options).every(key => Object.keys(defaults).includes(key))) {
    console.error('not all options are present!');
    console.error(Object.keys(options).filter(key => !Object.keys(defaults).includes(key)));
    return false;
  }

  function validStringOption(key, validOptions) {
    const result = validOptions.includes(options[key]);
    if (!result) console.error(`"${key}" is invalid, should be in ${validOptions}`);
    return result;
  }

  function validBoolOption(key) {
    const result = typeof options[key] === 'boolean';
    if (!result) console.error(`"${key}" option is invalid, should be boolean`);
    return result;
  }

  function validNumberOption(key, min, max) {
    const result =  typeof options[key] === 'number' &&
                           options[key] >= min &&
                           options[key] <= max;
    if (!result) console.error(`${key} should be between ${min} and ${max}`);
    return result;
  }

  if (!validStringOption('focus', ['original', 'new'])) return false;
  if (!validBoolOption('resizeOriginal')) return false;
  if (!validBoolOption('cloneOriginal')) return false;
  if (!validStringOption('clonePosition', ['clone-position-same',
                                            'clone-position-horizontal',
                                            'clone-position-vertical'])) return false;
  if (!validBoolOption('copyFullscreen')) return false;
  if (!validStringOption('menuButtonType', ['normal', 'popup'])) return false;

  const numberOpts = ['originalWidth', 'originalHeight', 'originalLeft',
                      'originalTop', 'newWidth', 'newHeight', 'newLeft',
                      'newTop'];
  if (numberOpts.some(opt => !validNumberOption(opt, 0.0, 1.0))) return false;

  return true;
}

function saveOptions(options) {
  if (!validateOptions(options)) return;

  chrome.storage.sync.set(options, () => {
    if (chrome.runtime.lastError !== undefined) {
      console.error(chrome.runtime.lastError);
    }
  });
}

// this returns defaults on fail
function loadOptions() {
  return new Promise((resolve, reject) => {
    chrome.storage.sync.get(defaults, options => {
      if (chrome.runtime.lastError === undefined) resolve(options);
      else reject(chrome.runtime.lastError);
    });
  });
}
<commit_msg>clearer method names<commit_after>/* eslint browser: true */
/* global chrome */
/* TODO this really wants to become a module when google fix the bug with them*/

const defaults = {
  // 'original' or 'new'
  focus: 'new',
  // boolean
  resizeOriginal: true,
  // boolean
  cloneOriginal: false,
  // clone-position-same', 'clone-position-horizontal', 'clone-position-vertical'
  clonePosition: 'clone-position-horizontal',
  // boolean
  copyFullscreen: true,
  // 'normal', 'popup'
  menuButtonType: 'normal',
  // these are all percentage 0.0 - 1.0
  originalWidth:  0.5,
  originalHeight: 1.0,
  originalLeft:   0.0,
  originalTop:    0.0,
  newWidth:       0.5,
  newHeight:      1.0,
  newLeft:        0.5,
  newTop:         0.0
};

// retrieve the localStorage key for a particular window property
// @windowId: 'original', 'new'
// @propertyKey: 'width', 'height', 'left', 'top'
function getStorageWindowPropKey(windowId, propKey) {
  return `${windowId}${propKey.slice(0).charAt().toUpperCase()}${propKey.slice(1)}`;
}

function validateOptions(options) {
  if (!Object.keys(options).every(key => Object.keys(defaults).includes(key))) {
    console.error('not all options are present!');
    console.error(Object.keys(options).filter(key => !Object.keys(defaults).includes(key)));
    return false;
  }

  function isValidStringOption(option, validOptions) {
    const result = validOptions.includes(options[option]);
    if (!result) console.error(`"${option}" is invalid, should be in ${validOptions}`);
    return result;
  }

  function isValidBoolOption(key) {
    const result = typeof options[key] === 'boolean';
    if (!result) console.error(`"${key}" option is invalid, should be boolean`);
    return result;
  }

  function isValidNumberOption(key, min, max) {
    const result = typeof options[key] === 'number' &&
                          options[key] >= min &&
                          options[key] <= max;
    if (!result) console.error(`${key} should be between ${min} and ${max}`);
    return result;
  }

  if (!isValidStringOption('focus', ['original', 'new'])) return false;
  if (!isValidBoolOption('resizeOriginal')) return false;
  if (!isValidBoolOption('cloneOriginal')) return false;
  if (!isValidStringOption('clonePosition', ['clone-position-same',
                                             'clone-position-horizontal',
                                             'clone-position-vertical'])) return false;
  if (!isValidBoolOption('copyFullscreen')) return false;
  if (!isValidStringOption('menuButtonType', ['normal', 'popup'])) return false;

  const numberOpts = ['originalWidth', 'originalHeight', 'originalLeft',
                      'originalTop', 'newWidth', 'newHeight', 'newLeft',
                      'newTop'];
  if (numberOpts.some(opt => !isValidNumberOption(opt, 0.0, 1.0))) return false;

  return true;
}

function saveOptions(options) {
  if (!validateOptions(options)) return;

  chrome.storage.sync.set(options, () => {
    if (chrome.runtime.lastError !== undefined) {
      console.error(chrome.runtime.lastError);
    }
  });
}

// this returns defaults on fail
function loadOptions() {
  return new Promise((resolve, reject) => {
    chrome.storage.sync.get(defaults, options => {
      if (chrome.runtime.lastError === undefined) resolve(options);
      else reject(chrome.runtime.lastError);
    });
  });
}
<|endoftext|>


<commit_before>/**
JavaScript Game framework for beginner.

oosamanet@gmail.com

*/
var unitaro={};

unitaro.scaler=function(id){
  var scale = function () {
    var container = document.querySelector('#'+id+'-canvas');
    var containerWidth = container.offsetWidth;
    var containerHeight = container.offsetHeight;

    var viewportWidth = document.documentElement.clientWidth;
    var viewportHeight = document.documentElement.clientHeight;

    var scaleWidth = viewportWidth / containerWidth;
    var scaleHeight = viewportHeight / containerHeight;
    var scaleBoth = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;

    var newContainerWidth = containerWidth * scaleBoth;
    var newContainerHeight = containerHeight * scaleBoth;

    var left = (viewportWidth - newContainerWidth) / 2;
    left = parseInt(left * (1 / scaleBoth), 10);

    var top = (viewportHeight - newContainerHeight) / 2;
    top = parseInt(top * (1 / scaleBoth), 10);

    // scale the whole container
    var transform = 'scale(' + scaleBoth + ',' + scaleBoth + ') translate(' + left + 'px, ' + top + 'px)';
    container.style['-webkit-transform-origin'] = 'top left 0';
    container.style['-webkit-transform'] = transform;
    container.style['transform-origin'] = 'top left 0';
    container.style['transform'] = transform;
    unitaro.scale=scaleBoth;
    unitaro.offset=[left*scaleBoth,top*scaleBoth];
  };

  window.onresize = scale;
  scale();
};

unitaro.dib={
  init: function(w,h,target){
    this.target=target;
    if (!this.target){
      this.target='unitaro-world';
    }
    var world = document.getElementById(this.target);
    if (!world) {
      world = document.createElement('div');
      world.id = this.target;
      document.body.appendChild(world);
    }
    this.canvas = document.createElement('canvas');
    this.canvas.id=this.target+"-canvas";
    this.canvas.width = w;
    this.canvas.height = h;
    world.appendChild(this.canvas);
    this.ctx = this.canvas.getContext('2d');
    this.ctx.textAlign = "center";
    this.ctx.font = "12px";
  },
  clear: function(x1,y1,w,h){
    this.ctx.clearRect(x1,y1,w,h);
  },
  box: function(x1,y1,w,h,color){
    if (color!=undefined){
      this.ctx.fillStyle = color;
    }
    this.ctx.fillRect(x1,y1,w,h);
  },
  textout: function(x1,y1,text,color,w){
    if (color!=undefined){
      this.ctx.fillStyle = color;
    }
    this.ctx.fillText(text,x1,y1,w);
  },
  sprites: {},
};

unitaro.TaskManager={
  task_list: [],
  type_list: {},
  add_task: function(task){
    this.task_list.push(task);
  },
  add_type: function(type,task){
    if (!this.type_list[type]){
      this.type_list[type]=[];
    }
    this.type_list[type].push(task);
  },
  del_type: function(type,task){
    if (!this.type_list[type]){
      this.type_list[type]=[];
    }
    this.delete_if(task,this.type_list[type]);
  },
  clear: function(){
    this.task_list=[];
    this.type_list={};
  },
  hitcheck_all: function(t1,t2){
    if (! this.type_list[t1] || ! this.type_list[t2]){
       return;
    }
    for (var i in this.type_list[t1]){
      var task1 = this.type_list[t1][i];
      var x1_1=task1.x-task1.w/2;
      var x1_2=x1_1+task1.w;
      var y1_1=task1.y-task1.h/2;
      var y1_2=y1_1+task1.h;
      var flag=false;
      for (var j in this.type_list[t2]){
        var task2 = this.type_list[t2][j];
        var x2_1=task2.x-task2.w/2;
        var x2_2=x2_1+task2.w;
        var y2_1=task2.y-task2.h/2;
        var y2_2=y2_1+task2.h;
        if (x2_2 <= x1_1 || x1_2 <= x2_1 ||
            y2_2 <= y1_1 || y1_2 <= y2_1){
           continue;
        }
        if (typeof task2.onhit == "function"){
          task2.onhit();
        }
        flag=true;
      }
      if (!flag){
        continue;
      }
      if (typeof task1.onhit == "function"){
        task1.onhit();
      }
    }
  },
  delete_if:function(target,array1){
    array1.some(function(v, i){
      if (v==target){
        array1.splice(i,1);
      }
    });
  },
  update_all: function(){
    var delete_list=[];
    for (var i in this.task_list){
      var t=this.task_list[i];
      if (t){
        var age=t.age++;
        t.update(age);
        if (!t.live){
          delete_list.push(t);
        }
      }
    }
    for (var i in delete_list){
      this.delete_if(delete_list[i],this.task_list);
      for (var j in this.type_list){
        this.delete_if(delete_list[i],this.type_list[j]);
      }
    }
  },
  call_all: function(mes){
    for (var i in this.task_list){
      var t=this.task_list[i];
      if ("function" == typeof(t[mes])){
        t[mes].apply(t,Array.prototype.slice.call(arguments,1));
      }
    }

  },
  call_all_with_hitcheck: function(mes,x,y){
    for (var i in this.task_list){
      var t=this.task_list[i];
      if (!t.hitcheck(x,y)){
        continue;
      }
      if ("function" == typeof(t[mes])){
        t[mes].apply(t,Array.prototype.slice.call(arguments,1));
      }
    }

  },
  draw_all: function(){
    this.call_all('draw');
  },
};

unitaro.Task=function(task){
  this.timer=0;
  this.type='';
  this.age=0;
  this.live=true;
  this.init=function(){};
  this.update=function(){};
  this.draw=function(){
    if (this.w > 0){
      unitaro.dib.box(this.x-this.w/2,this.y-this.h/2,this.w,this.h,this.color);
    }
  };
  for (i in task){
    this[i]=task[i];
  }

  unitaro.TaskManager.add_task(this);
  this.init.apply(this,Array.prototype.slice.call(arguments,1));
};
unitaro.Task.prototype={
  stop: function(){
    this.live=false;
    unitaro.TaskManager.del_type(this.type,this);
  },
  set_type: function(type){
    this.type=type;
    unitaro.TaskManager.add_type(type,this);
  },
  hitcheck: function(param1,param2){
    var x,y,w,h;
    if ("object" == typeof param1){
      x=param1.x;
      y=param1.y
      w=param1.w;
      h=param1.h;
    }else{
      x=param1;
      y=param2;
      w=1;
      h=1;
    }
    return (
      this.x-this.w/2 < x+w/2 &&
      this.x+this.w/2 > x-w/2 &&
      this.y-this.h/2 < y+h/2 &&
      this.y+this.h/2 > y-h/2
    );
  },
  onhit: function(){
    this.stop();
    console.dir("HIT "+this.type+" at "+this.x+","+this.y);
  }
};

unitaro.App=function(app){
  var self=this;
  self.init = function(){};
  self.update = function(){};
  self.draw = function(){};
  for (i in app){
    self[i]=app[i];
  }
  self.fps = self.fps || 20;

  unitaro.dib.init(self.WIDTH,self.HEIGHT,self.target);

  var _click = (window.ontouchstart === undefined)? 'click' : 'touchstart';
  window.addEventListener(_click,function(e){
    var x=e.pageX || e.touches[0].pageX;
    var y=e.pageY || e.touches[0].pageY;
    if (unitaro.scaler && unitaro.offset){
      x=x-unitaro.offset[0];
      y=y-unitaro.offset[1];
      x/=unitaro.scale;
      y/=unitaro.scale;
    }
    unitaro.TaskManager.call_all("onclick",x,y);
  });
  self.root=new unitaro.Task({onclick:function(x,y){self.onclick(x,y)}});

  var age=0;
  var loop = function(){
    setTimeout(function(){
      if (age==0){
        self.init();
      }
      self.update(age);

      unitaro.TaskManager.update_all();
      unitaro.TaskManager.draw_all();

      loop();
      age++;
    },1000/self.fps);
  };
  loop();
  if (!self.target){
    unitaro.scaler(unitaro.dib.target);
  }
};
<commit_msg>system call Task::onclick function only if Task bounding box clicked.<commit_after>/**
JavaScript Game framework for beginner.

oosamanet@gmail.com

*/
var unitaro={};

unitaro.scaler=function(id){
  var scale = function () {
    var container = document.querySelector('#'+id+'-canvas');
    var containerWidth = container.offsetWidth;
    var containerHeight = container.offsetHeight;

    var viewportWidth = document.documentElement.clientWidth;
    var viewportHeight = document.documentElement.clientHeight;

    var scaleWidth = viewportWidth / containerWidth;
    var scaleHeight = viewportHeight / containerHeight;
    var scaleBoth = (scaleHeight < scaleWidth) ? scaleHeight : scaleWidth;

    var newContainerWidth = containerWidth * scaleBoth;
    var newContainerHeight = containerHeight * scaleBoth;

    var left = (viewportWidth - newContainerWidth) / 2;
    left = parseInt(left * (1 / scaleBoth), 10);

    var top = (viewportHeight - newContainerHeight) / 2;
    top = parseInt(top * (1 / scaleBoth), 10);

    // scale the whole container
    var transform = 'scale(' + scaleBoth + ',' + scaleBoth + ') translate(' + left + 'px, ' + top + 'px)';
    container.style['-webkit-transform-origin'] = 'top left 0';
    container.style['-webkit-transform'] = transform;
    container.style['transform-origin'] = 'top left 0';
    container.style['transform'] = transform;
    unitaro.scale=scaleBoth;
    unitaro.offset=[left*scaleBoth,top*scaleBoth];
  };

  window.onresize = scale;
  scale();
};

unitaro.dib={
  init: function(w,h,target){
    this.target=target;
    if (!this.target){
      this.target='unitaro-world';
    }
    var world = document.getElementById(this.target);
    if (!world) {
      world = document.createElement('div');
      world.id = this.target;
      document.body.appendChild(world);
    }
    this.canvas = document.createElement('canvas');
    this.canvas.id=this.target+"-canvas";
    this.canvas.width = w;
    this.canvas.height = h;
    world.appendChild(this.canvas);
    this.ctx = this.canvas.getContext('2d');
    this.ctx.textAlign = "center";
    this.ctx.font = "12px";
  },
  clear: function(x1,y1,w,h){
    this.ctx.clearRect(x1,y1,w,h);
  },
  box: function(x1,y1,w,h,color){
    if (color!=undefined){
      this.ctx.fillStyle = color;
    }
    this.ctx.fillRect(x1,y1,w,h);
  },
  textout: function(x1,y1,text,color,w){
    if (color!=undefined){
      this.ctx.fillStyle = color;
    }
    this.ctx.fillText(text,x1,y1,w);
  },
  sprites: {},
};

unitaro.TaskManager={
  task_list: [],
  type_list: {},
  add_task: function(task){
    this.task_list.push(task);
  },
  add_type: function(type,task){
    if (!this.type_list[type]){
      this.type_list[type]=[];
    }
    this.type_list[type].push(task);
  },
  del_type: function(type,task){
    if (!this.type_list[type]){
      this.type_list[type]=[];
    }
    this.delete_if(task,this.type_list[type]);
  },
  clear: function(){
    this.task_list=[];
    this.type_list={};
  },
  hitcheck_all: function(t1,t2){
    if (! this.type_list[t1] || ! this.type_list[t2]){
       return;
    }
    for (var i in this.type_list[t1]){
      var task1 = this.type_list[t1][i];
      var x1_1=task1.x-task1.w/2;
      var x1_2=x1_1+task1.w;
      var y1_1=task1.y-task1.h/2;
      var y1_2=y1_1+task1.h;
      var flag=false;
      for (var j in this.type_list[t2]){
        var task2 = this.type_list[t2][j];
        var x2_1=task2.x-task2.w/2;
        var x2_2=x2_1+task2.w;
        var y2_1=task2.y-task2.h/2;
        var y2_2=y2_1+task2.h;
        if (x2_2 <= x1_1 || x1_2 <= x2_1 ||
            y2_2 <= y1_1 || y1_2 <= y2_1){
           continue;
        }
        if (typeof task2.onhit == "function"){
          task2.onhit();
        }
        flag=true;
      }
      if (!flag){
        continue;
      }
      if (typeof task1.onhit == "function"){
        task1.onhit();
      }
    }
  },
  delete_if:function(target,array1){
    array1.some(function(v, i){
      if (v==target){
        array1.splice(i,1);
      }
    });
  },
  update_all: function(){
    var delete_list=[];
    for (var i in this.task_list){
      var t=this.task_list[i];
      if (t!==undefined){
        t.age++;
      }
      var age=t.age;
      t.update(age);
      if (!t.live){
        delete_list.push(t);
      }
    }
    for (var i in delete_list){
      this.delete_if(delete_list[i],this.task_list);
      for (var j in this.type_list){
        this.delete_if(delete_list[i],this.type_list[j]);
      }
    }
  },
  is_inside: function(src,x,y,w,h){
    if (w===undefined){
      w=1;
      h=1;
    }
    return (src.x===undefined) || (
      src.x-src.w/2 < x+w/2 &&
      src.x+src.w/2 > x-w/2 &&
      src.y-src.h/2 < y+h/2 &&
      src.y+src.h/2 > y-h/2
    );
  },
  call_all: function(mes){
    for (var i in this.task_list){
      var t=this.task_list[i];
      if ("function" == typeof(t[mes])){
        t[mes].apply(t,Array.prototype.slice.call(arguments,1));
      }
    }

  },
  call_all_with_hitcheck: function(mes,x,y){
    for (var i in this.task_list){
      var t=this.task_list[i];
      if (!this.is_inside(t,x,y)){
        continue;
      }
      if ("function" == typeof(t[mes])){
        t[mes].apply(t,Array.prototype.slice.call(arguments,1));
      }
    }

  },
  draw_all: function(){
    this.call_all('draw');
  },
};

unitaro.Task=function(task){
  this.timer=0;
  this.type='';
  this.age=0;
  this.live=true;
  this.init=function(){};
  this.update=function(){};
  this.draw=function(){
    if (this.w > 0){
      unitaro.dib.box(this.x-this.w/2,this.y-this.h/2,this.w,this.h,this.color);
    }
  };
  for (i in task){
    this[i]=task[i];
  }

  unitaro.TaskManager.add_task(this);
  this.init.apply(this,Array.prototype.slice.call(arguments,1));
};
unitaro.Task.prototype={
  stop: function(){
    this.live=false;
    unitaro.TaskManager.del_type(this.type,this);
  },
  set_type: function(type){
    this.type=type;
    unitaro.TaskManager.add_type(type,this);
  },
  clear: function(){
    unitaro.TaskManager.clear();
  },
  onhit: function(){
    console.dir("BOMB "+this.type+" "+this.x+","+this.y);
  }
};

unitaro.App=function(app){
  var self=this;
  self.init = function(){};
  self.update = function(){};
  self.draw = function(){};
  for (i in app){
    self[i]=app[i];
  }
  self.fps = self.fps || 20;

  unitaro.dib.init(self.WIDTH,self.HEIGHT,self.target);

  var _click = (window.ontouchstart === undefined)? 'click' : 'touchstart';
  window.addEventListener(_click,function(e){
    var x=e.pageX || e.touches[0].pageX;
    var y=e.pageY || e.touches[0].pageY;
    if (unitaro.scaler && unitaro.offset){
      x=x-unitaro.offset[0];
      y=y-unitaro.offset[1];
      x/=unitaro.scale;
      y/=unitaro.scale;
    }
    unitaro.TaskManager.call_all_with_hitcheck("onclick",x,y);
  });
  self.root=new unitaro.Task({onclick:function(x,y){self.onclick(x,y)}});

  var age=0;
  var loop = function(){
    setTimeout(function(){
      if (age==0){
        self.init();
      }
      self.update(age);

      unitaro.TaskManager.update_all();
      unitaro.TaskManager.draw_all();

      loop();
      age++;
    },1000/self.fps);
  };
  loop();
  if (!self.target){
    unitaro.scaler(unitaro.dib.target);
  }
};
<|endoftext|>


<commit_before>/* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */

//
// Dalliance Genome Explorer
// (c) Thomas Down 2006-2010
//
// version.js
//

var VERSION = {
    CONFIG: 2,
    MAJOR:  0,
    MINOR:  5,
    MICRO:  1,
    PATCH:  '',
    BRANCH: 'das16'
}

VERSION.toString = function() {
    var vs = '' + this.MAJOR + '.' + this.MINOR + '.' + this.MICRO;
    if (this.PATCH) {
        vs = vs + this.PATCH;
    }
    if (this.BRANCH && this.BRANCH != '') {
        vs = vs + '-' + this.BRANCH;
    }
    return vs;
}
<commit_msg>Finished merging DAS 1.6<commit_after>/* -*- mode: javascript; c-basic-offset: 4; indent-tabs-mode: nil -*- */

//
// Dalliance Genome Explorer
// (c) Thomas Down 2006-2010
//
// version.js
//

var VERSION = {
    CONFIG: 2,
    MAJOR:  0,
    MINOR:  5,
    MICRO:  2,
    PATCH:  '',
    BRANCH: ''
}

VERSION.toString = function() {
    var vs = '' + this.MAJOR + '.' + this.MINOR + '.' + this.MICRO;
    if (this.PATCH) {
        vs = vs + this.PATCH;
    }
    if (this.BRANCH && this.BRANCH != '') {
        vs = vs + '-' + this.BRANCH;
    }
    return vs;
}
<|endoftext|>


<commit_before>var Cart = function(baseUrl) {
  var self = this;
  this.baseUrl = baseUrl;
  this.name = '';
  ko.track(this);
}

Cart.prototype = Object.create(Array.prototype)

Cart.prototype.doesntContain = function(doc) {
  var found = false;
  for (var i=0; i<this.length; ++i) {
    found = (this[i].id === doc.id ? true : found);
  }
  return !found;
}

Cart.prototype.add = function(doc) {
  if (this.doesntContain(doc)) {
    this.push(doc);
  }
}

Cart.prototype.addAll = function(docs) {
  for (var i = 0; i < docs.length; ++i) {
    this.add(docs[i]);
  }
}

Cart.prototype.drop = function(doc) {
  var index = this.indexOf(doc);
  if (index < 0) {
    return;
  }
  this.splice(index, 1);
}

Cart.prototype.dropAll = function() {
  while (this.length > 0) {
    this.splice(0, this.length);
  }
}

Cart.prototype.lectures = function() {
  lecs = [];
  for (var i = 0; i < this.length; ++i) {
    for (var j = 0; j < this[i].lectures.length; ++j) {
      if (lecs.indexOf(this[i].lectures[j]) === -1) {
        lecs.push(this[i].lectures[j]);
      }
    }
  }
  return lecs;
}

Cart.prototype.save = function() {
  var self = this;
  if (this.length === 0) {
    alert("Bitte wähle zuerst ein paar Protokolle oder Klausuren aus.");
    return;
  }
  if (this.name === '') {
    alert("Bitte gib' zuerst deinen Namen (oder ein Pseudonym) an.");
    return;
  }
  var docIDs = [];
  for (var i=0; i<self.length; i++) {
    docIDs.push(self[i].id);
  }
  $.ajax({
    url: this.baseUrl + '/data/carts/' + encodeURIComponent(this.name),
    type: 'POST',
    contentType: 'application/json; charset=UTF-8',
    data: JSON.stringify(docIDs),
    success: function() {
      self.splice(0, self.length);
    }
  })
}

Cart.prototype.includesOral = function() {
  for (var i = 0; i < this.length; ++i) {
    if (this[i].examType === "oral") {
      return true;
    }
  }
  return false;
}

Cart.prototype.priceEstimate = function(depositCount) {
  var price = 0;
  for (var i = 0; i < this.length; ++i) {
    price += this[i].pages * pricePerPage;
  }
  if (depositCount === undefined) {
    if (this.includesOral()) {
      price += depositPrice;
    }
  }
  else {
    price += depositCount * depositPrice;
  }

  // round to next-highest ten-cent unit
  return Math.ceil(price / 10) * 10;
}

Cart.prototype.displayDate = function() {
  var months = ['Jan ',
                'Feb ',
                'Mär ',
                'Apr ',
                'Mai ',
                'Jun ',
                'Jul ',
                'Aug ',
                'Sep ',
                'Okt ',
                'Nov ',
                'Dez '];
  var d = new Date(this.date);
  return d.getDate() + '. ' + months[d.getMonth()] + d.getFullYear();
}

<commit_msg>Show cart creation time of day (fixes #26)<commit_after>var Cart = function(baseUrl) {
  var self = this;
  this.baseUrl = baseUrl;
  this.name = '';
  ko.track(this);
}

Cart.prototype = Object.create(Array.prototype)

Cart.prototype.doesntContain = function(doc) {
  var found = false;
  for (var i=0; i<this.length; ++i) {
    found = (this[i].id === doc.id ? true : found);
  }
  return !found;
}

Cart.prototype.add = function(doc) {
  if (this.doesntContain(doc)) {
    this.push(doc);
  }
}

Cart.prototype.addAll = function(docs) {
  for (var i = 0; i < docs.length; ++i) {
    this.add(docs[i]);
  }
}

Cart.prototype.drop = function(doc) {
  var index = this.indexOf(doc);
  if (index < 0) {
    return;
  }
  this.splice(index, 1);
}

Cart.prototype.dropAll = function() {
  while (this.length > 0) {
    this.splice(0, this.length);
  }
}

Cart.prototype.lectures = function() {
  lecs = [];
  for (var i = 0; i < this.length; ++i) {
    for (var j = 0; j < this[i].lectures.length; ++j) {
      if (lecs.indexOf(this[i].lectures[j]) === -1) {
        lecs.push(this[i].lectures[j]);
      }
    }
  }
  return lecs;
}

Cart.prototype.save = function() {
  var self = this;
  if (this.length === 0) {
    alert("Bitte wähle zuerst ein paar Protokolle oder Klausuren aus.");
    return;
  }
  if (this.name === '') {
    alert("Bitte gib' zuerst deinen Namen (oder ein Pseudonym) an.");
    return;
  }
  var docIDs = [];
  for (var i=0; i<self.length; i++) {
    docIDs.push(self[i].id);
  }
  $.ajax({
    url: this.baseUrl + '/data/carts/' + encodeURIComponent(this.name),
    type: 'POST',
    contentType: 'application/json; charset=UTF-8',
    data: JSON.stringify(docIDs),
    success: function() {
      self.splice(0, self.length);
    }
  })
}

Cart.prototype.includesOral = function() {
  for (var i = 0; i < this.length; ++i) {
    if (this[i].examType === "oral") {
      return true;
    }
  }
  return false;
}

Cart.prototype.priceEstimate = function(depositCount) {
  var price = 0;
  for (var i = 0; i < this.length; ++i) {
    price += this[i].pages * pricePerPage;
  }
  if (depositCount === undefined) {
    if (this.includesOral()) {
      price += depositPrice;
    }
  }
  else {
    price += depositCount * depositPrice;
  }

  // round to next-highest ten-cent unit
  return Math.ceil(price / 10) * 10;
}

Cart.prototype.displayDate = function() {
  var months = ['Jan ',
                'Feb ',
                'Mär ',
                'Apr ',
                'Mai ',
                'Jun ',
                'Jul ',
                'Aug ',
                'Sep ',
                'Okt ',
                'Nov ',
                'Dez '];
  var d = new Date(this.date);
  return d.getDate() + '. ' + months[d.getMonth()] + d.getFullYear() + ' ' + d.getHours() + ':' + d.getMinutes();
}

<|endoftext|>


<commit_before>/**
 * Created by AsherS on 8/24/15.
 */

var _ = require('underscore');
var logger = require('./logger/logger')(module);
var flavorDownloader = require('./FlavorDownloader');
var masterManifestGeneratorCreator = require('./MasterManifestGenerator');
var Q = require('q');

var EntryDownloader = (function () {

    function EntryDownloader(entryId, hostname, port, applicationName, dvrWindow) {
        this.entryId = entryId;
        this.dvrWindow = dvrWindow;
        this.flavorsDownloaders = [];
        this.masterManifestGenerator = masterManifestGeneratorCreator(entryId, hostname, port, applicationName);
    }

    EntryDownloader.prototype.start = function () {
        var persistenceFormat = require('./PersistenceFormat');
        var that = this;
        logger.info("Starting entry downloader for entryId: " + that.entryId + "\n");
        return that.masterManifestGenerator.getAllFlavors()
            .then(function (flavors) {
                that.flavorsDownloaders = _.map(flavors, function (flavor) {

                    var playlistName = persistenceFormat.getManifestName();
                    var bitrate = flavor.bandwidth;
                    var destPath = persistenceFormat.getFlavorFullPath(that.entryId, bitrate);
                    logger.info("EntryId: " + that.entryId + " - creating flavor downloader: " + bitrate);
                    return new flavorDownloader(flavor.liveURL, destPath, that.entryId, bitrate, playlistName, that.dvrWindow);
                });

                //start the download
                var promises = _.map(that.flavorsDownloaders, function (f) {
                    logger.info("EntryId: " + that.entryId + " - starting flavor downloader: " + f.flavor);
                    return f.start();
                });

                return Q.allSettled(promises);

            }).then(function (results) {
                var failedFlavors = _.filter(results, function (result) {
                    return result.state === "rejected";
                });

                if (failedFlavors.length === results.length) {
                    throw new Error("Failed to start entry downloader for entry: " + that.entryId + ". All flavor downloaders failed");
                }

                if (failedFlavors.length > 0)
                {
                    var message = 'Could not start the following flavors for entry id: ' + this.entryId + '\n';
                    message += _.map(failedFlavors, function(err){
                        return err.flavor;
                    }).toString();
                    logger.warn(message);
                }
            });
    };

    EntryDownloader.prototype.stop = function () {
        logger.info('stopping entry downloader for entry id: ' + this.entryId);
        var that = this;
        var stopPromises =_.map(this.flavorsDownloaders, function (downloader) {
            return downloader.stop();
        });

        return Q.all(stopPromises).then(function(){
            logger.info('successfully stopped entry downloader for entry id: ' + that.entryId);
        }, function(err){
            logger.error('failed stopping flavor downloader for entry id: ' + that.entryId + ' and flavor ' + downloader.flavor + "\n" + err + "\n" + err.stack);
            throw err;
        });
    };

    return EntryDownloader;
})();

module.exports = EntryDownloader;<commit_msg>aggregate errors<commit_after>/**
 * Created by AsherS on 8/24/15.
 */

var _ = require('underscore');
var logger = require('./logger/logger')(module);
var flavorDownloader = require('./FlavorDownloader');
var masterManifestGeneratorCreator = require('./MasterManifestGenerator');
var Q = require('q');
var ErrorUtils = require('./utils/error-utils');

var EntryDownloader = (function () {

    function EntryDownloader(entryId, hostname, port, applicationName, dvrWindow) {
        this.entryId = entryId;
        this.dvrWindow = dvrWindow;
        this.flavorsDownloaders = [];
        this.masterManifestGenerator = masterManifestGeneratorCreator(entryId, hostname, port, applicationName);
    }

    EntryDownloader.prototype.start = function () {
        var persistenceFormat = require('./PersistenceFormat');
        var that = this;
        logger.info("Starting entry downloader for entryId: " + that.entryId + "\n");
        return that.masterManifestGenerator.getAllFlavors()
            .then(function (flavors) {
                that.flavorsDownloaders = _.map(flavors, function (flavor) {

                    var playlistName = persistenceFormat.getManifestName();
                    var bitrate = flavor.bandwidth;
                    var destPath = persistenceFormat.getFlavorFullPath(that.entryId, bitrate);
                    logger.info("EntryId: " + that.entryId + " - creating flavor downloader: " + bitrate);
                    return new flavorDownloader(flavor.liveURL, destPath, that.entryId, bitrate, playlistName, that.dvrWindow);
                });

                //start the download
                var promises = _.map(that.flavorsDownloaders, function (f) {
                    logger.info("EntryId: " + that.entryId + " - starting flavor downloader: " + f.flavor);
                    return f.start();
                });

                return Q.allSettled(promises);

            }).then(function (results) {

                var errs = ErrorUtils.aggregateErrors(results);

                if (errs.numErrors === results.length) {
                    throw new Error("Failed to start entry downloader for entry: " + that.entryId + ". All flavor downloaders failed\n" + errs.err);
                }

                if (errs.numErrors > 0) {
                    logger.error('Failed to start entry downloader:\n',errs.err);
                }

            });
    };

    EntryDownloader.prototype.stop = function () {
        logger.info('stopping entry downloader for entry id: ' + this.entryId);
        var that = this;
        var stopPromises =_.map(this.flavorsDownloaders, function (downloader) {
            return downloader.stop();
        });

        return Q.all(stopPromises).then(function(){
            logger.info('successfully stopped entry downloader for entry id: ' + that.entryId);
        }, function(err){
            logger.error('failed stopping flavor downloader for entry id: ' + that.entryId + ' and flavor ' + downloader.flavor + "\n" + err + "\n" + err.stack);
            throw err;
        });
    };

    return EntryDownloader;
})();

module.exports = EntryDownloader;<|endoftext|>


<commit_before>/**
 * Copyright (C) 2019-2020  The Software Heritage developers
 * See the AUTHORS file at the top-level directory of this distribution
 * License: GNU Affero General Public License version 3, or any later version
 * See top-level LICENSE file for more information
 */

let url;
let origin;
const $ = Cypress.$;

const saveCodeMsg = {
  'success': 'The "save code now" request has been accepted and will be processed as soon as possible.',
  'warning': 'The "save code now" request has been put in pending state and may be accepted for processing after manual review.',
  'rejected': 'The "save code now" request has been rejected because the provided origin url is blacklisted.',
  'rateLimit': 'The rate limit for "save code now" requests has been reached. Please try again later.',
  'unknownError': 'An unexpected error happened when submitting the "save code now request',
  'csrfError': 'CSRF Failed: Referrer checking failed - no Referrer.'
};

function makeOriginSaveRequest(originType, originUrl) {
  cy.get('#swh-input-origin-url')
    .type(originUrl)
    .get('#swh-input-visit-type')
    .select(originType)
    .get('#swh-save-origin-form')
    .submit();
}

function checkAlertVisible(alertType, msg) {
  cy.get('#swh-origin-save-request-status')
    .should('be.visible')
    .find(`.alert-${alertType}`)
    .should('be.visible')
    .and('contain', msg);
}

// Stub requests to save an origin
function stubSaveRequest(requestUrl, objectType, status, originUrl, taskStatus,
                         responseStatus = 200, errorMessage = '') {
  let response;
  if (responseStatus !== 200 && errorMessage) {
    response = {'detail': errorMessage};
  } else {
    response = genOriginSaveResponse(objectType, status, originUrl, Date().toString(), taskStatus);
  }
  cy.route({
    method: 'POST',
    status: responseStatus,
    url: requestUrl,
    response: response
  }).as('saveRequest');
}

// Mocks API response : /save/(:object_type)/(:origin_url)
// object_type : {'git', 'hg', 'svn'}
function genOriginSaveResponse(objectType, saveRequestStatus, originUrl, saveRequestDate, saveTaskStatus, visitDate = Date().toString()) {
  return {
    'visit_type': objectType,
    'save_request_status': saveRequestStatus,
    'origin_url': originUrl,
    'id': 1,
    'save_request_date': saveRequestDate,
    'save_task_status': saveTaskStatus,
    'visit_date': visitDate
  };
};

describe('Origin Save Tests', function() {
  before(function() {
    url = this.Urls.origin_save();
    origin = this.origin[0];
    this.originSaveUrl = this.Urls.origin_save_request(origin.type, origin.url);
  });

  beforeEach(function() {
    cy.fixture('origin-save').as('originSaveJSON');
    cy.fixture('save-task-info').as('saveTaskInfoJSON');
    cy.visit(url);
    cy.server();
  });

  it('should display accepted message when accepted', function() {
    stubSaveRequest(this.originSaveUrl, origin.type, 'accepted',
                    origin.url, 'not yet scheduled');

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('success', saveCodeMsg['success']);
    });
  });

  it('should validate gitlab subproject url', function() {
    const gitlabSubProjectUrl = 'https://gitlab.com/user/project/sub/';
    const originSaveUrl = this.Urls.origin_save_request('git', gitlabSubProjectUrl);
    stubSaveRequest(originSaveUrl, 'git', 'accepted',
                    gitlabSubProjectUrl, 'not yet scheduled');

    makeOriginSaveRequest('git', gitlabSubProjectUrl);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('success', saveCodeMsg['success']);
    });
  });

  it('should display warning message when pending', function() {
    stubSaveRequest(this.originSaveUrl, origin.type, 'pending',
                    origin.url, 'not created');

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('warning', saveCodeMsg['warning']);
    });
  });

  it('should show error when csrf validation failed (status: 403)', function() {
    stubSaveRequest(this.originSaveUrl, origin.type, 'rejected',
                    origin.url, 'not created', 403, saveCodeMsg['csrfError']);

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['csrfError']);
    });
  });

  it('should show error when origin is rejected (status: 403)', function() {
    stubSaveRequest(this.originSaveUrl, origin.type, 'rejected',
                    origin.url, 'not created', 403, saveCodeMsg['rejected']);

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['rejected']);
    });
  });

  it('should show error when rate limited (status: 429)', function() {
    stubSaveRequest(this.originSaveUrl, origin.type,
                    'Request was throttled. Expected available in 60 seconds.',
                    origin.url, 'not created', 429);

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['rateLimit']);
    });
  });

  it('should show error when unknown error occurs (status other than 200, 403, 429)', function() {
    stubSaveRequest(this.originSaveUrl, origin.type, 'Error',
                    origin.url, 'not created', 406);

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['unknownError']);
    });
  });

  it('should display origin save info in the requests table', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('tbody tr').then(rows => {
      let i = 0;
      for (let row of rows) {
        const cells = row.cells;
        const requestDateStr = new Date(this.originSaveJSON.data[i].save_request_date).toLocaleString();
        const saveStatus = this.originSaveJSON.data[i].save_task_status;
        assert.equal($(cells[0]).text(), requestDateStr);
        assert.equal($(cells[1]).text(), this.originSaveJSON.data[i].visit_type);
        let html = '';
        if (saveStatus === 'succeed') {
          let browseOriginUrl = `${this.Urls.browse_origin()}?origin_url=${this.originSaveJSON.data[i].origin_url}`;
          browseOriginUrl += `&timestamp=${encodeURIComponent(this.originSaveJSON.data[i].visit_date)}`;
          html += `<a href="${browseOriginUrl}">${this.originSaveJSON.data[i].origin_url}</a>`;
        } else {
          html += this.originSaveJSON.data[i].origin_url;
        }
        html += ` <a href="${this.originSaveJSON.data[i].origin_url}">`;
        html += '';
        assert.equal($(cells[2]).html(), html);
        assert.equal($(cells[3]).text(), this.originSaveJSON.data[i].save_request_status);
        assert.equal($(cells[4]).text(), saveStatus);
        ++i;
      }
    });
  });

  it('should not add timestamp to the browse origin URL is no visit date has been found', function() {
    const originUrl = 'https://git.example.org/example.git';
    const saveRequestData = genOriginSaveResponse('git', 'accepted', originUrl, Date().toString(), 'succeed', null);
    const saveRequestsListData = {
      'recordsTotal': 1,
      'draw': 2,
      'recordsFiltered': 1,
      'data': [saveRequestData]
    };
    cy.route('GET', '/save/requests/list/**', saveRequestsListData);
    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('tbody tr').then(rows => {
      const firstRowCells = rows[0].cells;
      const browseOriginUrl = `${this.Urls.browse_origin()}?origin_url=${originUrl}`;
      const browseOriginLink = `<a href="${browseOriginUrl}">${originUrl}</a>`;
      expect($(firstRowCells[2]).html()).to.have.string(browseOriginLink);
    });
  });

  it('should display/close task info popover when clicking on the info button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.route('GET', '/save/task/info/**', '@saveTaskInfoJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('be.visible');

    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('not.be.visible');
  });

  it('should hide task info popover when clicking on the close button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.route('GET', '/save/task/info/**', '@saveTaskInfoJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('be.visible');

    cy.get('.swh-save-request-info-close')
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('not.be.visible');
  });

  it('should fill save request form when clicking on "Save again" button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-origin-again')
      .eq(0)
      .click();

    cy.get('tbody tr').eq(0).then(row => {
      const cells = row[0].cells;
      cy.get('#swh-input-visit-type')
        .should('have.value', $(cells[1]).text());
      cy.get('#swh-input-origin-url')
        .should('have.value', $(cells[2]).text().slice(0, -1));
    });
  });

  it('should select correct origin type if possible when clicking on "Save again" button', function() {
    const originUrl = 'https://gitlab.inria.fr/solverstack/maphys/maphys/';
    const badOriginType = 'hg';
    const goodOriginType = 'git';
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    stubSaveRequest(this.Urls.origin_save_request(badOriginType, originUrl),
                    badOriginType, 'accepted',
                    originUrl, 'failed', 200, saveCodeMsg['accepted']);

    makeOriginSaveRequest(badOriginType, originUrl);

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.wait('@saveRequest').then(() => {
      cy.get('.swh-save-origin-again')
        .eq(0)
        .click();

      cy.get('tbody tr').eq(0).then(row => {
        const cells = row[0].cells;
        cy.get('#swh-input-visit-type')
          .should('have.value', goodOriginType);
        cy.get('#swh-input-origin-url')
          .should('have.value', $(cells[2]).text().slice(0, -1));
      });
    });
  });

});
<commit_msg>cypress/origin-save: Improve tests implementation<commit_after>/**
 * Copyright (C) 2019-2020  The Software Heritage developers
 * See the AUTHORS file at the top-level directory of this distribution
 * License: GNU Affero General Public License version 3, or any later version
 * See top-level LICENSE file for more information
 */

let url;
let origin;
const $ = Cypress.$;

const saveCodeMsg = {
  'success': 'The "save code now" request has been accepted and will be processed as soon as possible.',
  'warning': 'The "save code now" request has been put in pending state and may be accepted for processing after manual review.',
  'rejected': 'The "save code now" request has been rejected because the provided origin url is blacklisted.',
  'rateLimit': 'The rate limit for "save code now" requests has been reached. Please try again later.',
  'unknownError': 'An unexpected error happened when submitting the "save code now request',
  'csrfError': 'CSRF Failed: Referrer checking failed - no Referrer.'
};

function makeOriginSaveRequest(originType, originUrl) {
  cy.get('#swh-input-origin-url')
    .type(originUrl)
    .get('#swh-input-visit-type')
    .select(originType)
    .get('#swh-save-origin-form')
    .submit();
}

function checkAlertVisible(alertType, msg) {
  cy.get('#swh-origin-save-request-status')
    .should('be.visible')
    .find(`.alert-${alertType}`)
    .should('be.visible')
    .and('contain', msg);
}

// Stub requests to save an origin
function stubSaveRequest({
  requestUrl,
  visitType = 'git',
  saveRequestStatus,
  originUrl,
  saveTaskStatus,
  responseStatus = 200,
  errorMessage = '',
  saveRequestDate = new Date(),
  visitDate = new Date()
} = {}) {
  let response;
  if (responseStatus !== 200 && errorMessage) {
    response = {
      'detail': errorMessage
    };
  } else {
    response = genOriginSaveResponse({visitType: visitType,
                                      saveRequestStatus: saveRequestStatus,
                                      originUrl: originUrl,
                                      saveRequestDate: saveRequestDate,
                                      saveTaskStatus: saveTaskStatus,
                                      visitDate: visitDate});
  }
  cy.route({
    method: 'POST',
    status: responseStatus,
    url: requestUrl,
    response: response
  }).as('saveRequest');
}

// Mocks API response : /save/(:visit_type)/(:origin_url)
// visit_type : {'git', 'hg', 'svn'}
function genOriginSaveResponse({
  visitType = 'git',
  saveRequestStatus,
  originUrl,
  saveRequestDate = new Date(),
  saveTaskStatus,
  visitDate = new Date()
} = {}) {
  return {
    'visit_type': visitType,
    'save_request_status': saveRequestStatus,
    'origin_url': originUrl,
    'id': 1,
    'save_request_date': saveRequestDate ? saveRequestDate.toISOString() : null,
    'save_task_status': saveTaskStatus,
    'visit_date': visitDate ? visitDate.toISOString() : null
  };
};

describe('Origin Save Tests', function() {
  before(function() {
    url = this.Urls.origin_save();
    origin = this.origin[0];
    this.originSaveUrl = this.Urls.origin_save_request(origin.type, origin.url);
  });

  beforeEach(function() {
    cy.fixture('origin-save').as('originSaveJSON');
    cy.fixture('save-task-info').as('saveTaskInfoJSON');
    cy.visit(url);
    cy.server();
  });

  it('should display accepted message when accepted', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'accepted',
                     originUrl: origin.url,
                     saveTaskStatus: 'not yet scheduled'});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('success', saveCodeMsg['success']);
    });
  });

  it('should validate gitlab subproject url', function() {
    const gitlabSubProjectUrl = 'https://gitlab.com/user/project/sub/';
    const originSaveUrl = this.Urls.origin_save_request('git', gitlabSubProjectUrl);

    stubSaveRequest({requestUrl: originSaveUrl,
                     saveRequestStatus: 'accepted',
                     originurl: gitlabSubProjectUrl,
                     saveTaskStatus: 'not yet scheduled'});

    makeOriginSaveRequest('git', gitlabSubProjectUrl);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('success', saveCodeMsg['success']);
    });
  });

  it('should display warning message when pending', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'pending',
                     originUrl: origin.url,
                     saveTaskStatus: 'not created'});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('warning', saveCodeMsg['warning']);
    });
  });

  it('should show error when csrf validation failed (status: 403)', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'rejected',
                     originUrl: origin.url,
                     saveTaskStatus: 'not created',
                     responseStatus: 403,
                     errorMessage: saveCodeMsg['csrfError']});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['csrfError']);
    });
  });

  it('should show error when origin is rejected (status: 403)', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'rejected',
                     originUrl: origin.url,
                     saveTaskStatus: 'not created',
                     responseStatus: 403,
                     errorMessage: saveCodeMsg['rejected']});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['rejected']);
    });
  });

  it('should show error when rate limited (status: 429)', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'Request was throttled. Expected available in 60 seconds.',
                     originUrl: origin.url,
                     saveTaskStatus: 'not created',
                     responseStatus: 429});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['rateLimit']);
    });
  });

  it('should show error when unknown error occurs (status other than 200, 403, 429)', function() {
    stubSaveRequest({requestUrl: this.originSaveUrl,
                     saveRequestStatus: 'Error',
                     originUrl: origin.url,
                     saveTaskStatus: 'not created',
                     responseStatus: 406});

    makeOriginSaveRequest(origin.type, origin.url);

    cy.wait('@saveRequest').then(() => {
      checkAlertVisible('danger', saveCodeMsg['unknownError']);
    });
  });

  it('should display origin save info in the requests table', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('tbody tr').then(rows => {
      let i = 0;
      for (let row of rows) {
        const cells = row.cells;
        const requestDateStr = new Date(this.originSaveJSON.data[i].save_request_date).toLocaleString();
        const saveStatus = this.originSaveJSON.data[i].save_task_status;
        assert.equal($(cells[0]).text(), requestDateStr);
        assert.equal($(cells[1]).text(), this.originSaveJSON.data[i].visit_type);
        let html = '';
        if (saveStatus === 'succeed') {
          let browseOriginUrl = `${this.Urls.browse_origin()}?origin_url=${this.originSaveJSON.data[i].origin_url}`;
          browseOriginUrl += `&timestamp=${encodeURIComponent(this.originSaveJSON.data[i].visit_date)}`;
          html += `<a href="${browseOriginUrl}">${this.originSaveJSON.data[i].origin_url}</a>`;
        } else {
          html += this.originSaveJSON.data[i].origin_url;
        }
        html += ` <a href="${this.originSaveJSON.data[i].origin_url}">`;
        html += '';
        assert.equal($(cells[2]).html(), html);
        assert.equal($(cells[3]).text(), this.originSaveJSON.data[i].save_request_status);
        assert.equal($(cells[4]).text(), saveStatus);
        ++i;
      }
    });
  });

  it('should not add timestamp to the browse origin URL is no visit date has been found', function() {
    const originUrl = 'https://git.example.org/example.git';
    const saveRequestData = genOriginSaveResponse({
      saveRequestStatus: 'accepted',
      originUrl: originUrl,
      saveTaskStatus: 'succeed',
      visitDate: null
    });
    const saveRequestsListData = {
      'recordsTotal': 1,
      'draw': 2,
      'recordsFiltered': 1,
      'data': [saveRequestData]
    };
    cy.route('GET', '/save/requests/list/**', saveRequestsListData);
    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('tbody tr').then(rows => {
      const firstRowCells = rows[0].cells;
      const browseOriginUrl = `${this.Urls.browse_origin()}?origin_url=${originUrl}`;
      const browseOriginLink = `<a href="${browseOriginUrl}">${originUrl}</a>`;
      expect($(firstRowCells[2]).html()).to.have.string(browseOriginLink);
    });
  });

  it('should display/close task info popover when clicking on the info button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.route('GET', '/save/task/info/**', '@saveTaskInfoJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('be.visible');

    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('not.be.visible');
  });

  it('should hide task info popover when clicking on the close button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    cy.route('GET', '/save/task/info/**', '@saveTaskInfoJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-request-info')
      .eq(0)
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('be.visible');

    cy.get('.swh-save-request-info-close')
      .click();

    cy.get('.swh-save-request-info-popover')
      .should('not.be.visible');
  });

  it('should fill save request form when clicking on "Save again" button', function() {
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.get('.swh-save-origin-again')
      .eq(0)
      .click();

    cy.get('tbody tr').eq(0).then(row => {
      const cells = row[0].cells;
      cy.get('#swh-input-visit-type')
        .should('have.value', $(cells[1]).text());
      cy.get('#swh-input-origin-url')
        .should('have.value', $(cells[2]).text().slice(0, -1));
    });
  });

  it('should select correct visit type if possible when clicking on "Save again" button', function() {
    const originUrl = 'https://gitlab.inria.fr/solverstack/maphys/maphys/';
    const badVisitType = 'hg';
    const goodVisitType = 'git';
    cy.route('GET', '/save/requests/list/**', '@originSaveJSON');
    stubSaveRequest({requestUrl: this.Urls.origin_save_request(badVisitType, originUrl),
                     visitType: badVisitType,
                     saveRequestStatus: 'accepted',
                     originUrl: originUrl,
                     saveTaskStatus: 'failed',
                     responseStatus: 200,
                     errorMessage: saveCodeMsg['accepted']});

    makeOriginSaveRequest(badVisitType, originUrl);

    cy.get('#swh-origin-save-requests-list-tab').click();
    cy.wait('@saveRequest').then(() => {
      cy.get('.swh-save-origin-again')
        .eq(0)
        .click();

      cy.get('tbody tr').eq(0).then(row => {
        const cells = row[0].cells;
        cy.get('#swh-input-visit-type')
          .should('have.value', goodVisitType);
        cy.get('#swh-input-origin-url')
          .should('have.value', $(cells[2]).text().slice(0, -1));
      });
    });
  });

});
<|endoftext|>