metamaps--metamaps/app/assets/javascripts/src/Metamaps.Import.js.erb
Devin Howard 387c863222 fix a bug with synapses and use cid to link new topics with synapses
Synapses are now created client-side, but still rejected server-side
2016-03-25 16:48:14 +08:00

293 lines
8.9 KiB
Text

/*
* Example tab-separated input:
* Some fields will be ignored
*
* Topics
* Id Name Metacode X Y Description Link User Permission
* 8 topic8 Action -231 131 admin commons
* 5 topic Action -229 -131 admin commons
* 7 topic7.1 Action -470 -55 hey admin commons
* 2 topic2 Event -57 -63 admin commons
* 1 topic1 Catalyst -51 50 admin commons
* 6 topic6 Action -425 63 admin commons
*
* Synapses
* Id Description Category Topic1 Topic2 User Permission
* 43 from-to 6 2 admin commons
* 44 from-to 6 1 admin commons
* 45 from-to 6 5 admin commons
* 46 from-to 2 7 admin commons
* 47 from-to 8 6 admin commons
* 48 from-to 8 1 admin commons
*
*/
Metamaps.Import = {
topicWhitelist: [
'id', 'name', 'metacode', 'description', 'link', 'permission'
],
synapseWhitelist: [
'id', 'desc', 'description', 'category', 'topic1', 'topic2', 'permission'
],
cidMappings: {}, //to be filled by import_id => cid mappings
init: function() {
var self = Metamaps.Import;
$('body').bind('paste', function(e) {
var text = e.originalEvent.clipboardData.getData('text/plain');
var results = self.parseTabbedString(text);
if (results === false) return;
var topics = results.topics;
var synapses = results.synapses;
if (topics.length > 0 || synapses.length > 0) {
if (confirm("Are you sure you want to create " + topics.length +
" new topics and " + synapses.length + " new synapses?")) {
self.importTopics(topics);
self.importSynapses(synapses);
}//if
}//if
});
},
abort: function(message) {
alert("Sorry, something went wrong!\n\n" + message);
console.error(message);
},
simplify: function(string) {
return string
.replace(/(^\s*|\s*$)/g, '')
.toLowerCase();
},
parseTabbedString: function(text) {
var self = Metamaps.Import;
// determine line ending and split lines
var delim = "\n";
if (text.indexOf("\r\n") !== -1) {
delim = "\r\n";
} else if (text.indexOf("\r") !== -1) {
delim = "\r";
}//if
var STATES = {
ABORT: -1,
UNKNOWN: 0,
TOPICS_NEED_HEADERS: 1,
SYNAPSES_NEED_HEADERS: 2,
TOPICS: 3,
SYNAPSES: 4,
};
// state & lines determine parser behaviour
var state = STATES.UNKNOWN;
var lines = text.split(delim);
var results = { topics: [], synapses: [] }
var topicHeaders = [];
var synapseHeaders = [];
lines.forEach(function(line_raw, index) {
var line = line_raw.split("\t");
var noblanks = line.filter(function(elt) {
return elt !== "";
});
switch(state) {
case STATES.UNKNOWN:
if (noblanks.length === 0) {
state = STATES.UNKNOWN;
break;
} else if (noblanks.length === 1 && self.simplify(line[0]) === 'topics') {
state = STATES.TOPICS_NEED_HEADERS;
break;
} else if (noblanks.length === 1 && self.simplify(line[0]) === 'synapses') {
state = STATES.SYNAPSES_NEED_HEADERS;
break;
}
state = STATES.TOPICS_NEED_HEADERS;
// FALL THROUGH - if we're not sure what to do, pretend
// we're on the TOPICS_NEED_HEADERS state and parse some headers
case STATES.TOPICS_NEED_HEADERS:
if (noblanks.length < 2) {
self.abort("Not enough topic headers on line " + index);
state = STATES.ABORT;
}
topicHeaders = line.map(function(header, index) {
return header.toLowerCase().replace('description', 'desc');
});
state = STATES.TOPICS;
break;
case STATES.SYNAPSES_NEED_HEADERS:
if (noblanks.length < 2) {
self.abort("Not enough synapse headers on line " + index);
state = STATES.ABORT;
}
synapseHeaders = line.map(function(header, index) {
return header.toLowerCase().replace('description', 'desc');
});
state = STATES.SYNAPSES;
break;
case STATES.TOPICS:
if (noblanks.length === 0) {
state = STATES.UNKNOWN;
} else if (noblanks.length === 1 && line[0].toLowerCase() === 'topics') {
state = STATES.TOPICS_NEED_HEADERS;
} else if (noblanks.length === 1 && line[0].toLowerCase() === 'synapses') {
state = STATES.SYNAPSES_NEED_HEADERS;
} else {
var topic = {};
line.forEach(function(field, index) {
var header = topicHeaders[index];
if (self.topicWhitelist.indexOf(header) === -1) return;
topic[header] = field;
if (['id', 'x', 'y'].indexOf(header) !== -1) {
topic[header] = parseInt(topic[header]);
}//if
});
results.topics.push(topic);
}
break;
case STATES.SYNAPSES:
if (noblanks.length === 0) {
state = STATES.UNKNOWN;
} else if (noblanks.length === 1 && line[0].toLowerCase() === 'topics') {
state = STATES.TOPICS_NEED_HEADERS;
} else if (noblanks.length === 1 && line[0].toLowerCase() === 'synapses') {
state = STATES.SYNAPSES_NEED_HEADERS;
} else {
var synapse = {};
line.forEach(function(field, index) {
var header = synapseHeaders[index];
if (self.synapseWhitelist.indexOf(header) === -1) return;
synapse[header] = field;
if (['id', 'topic1', 'topic2'].indexOf(header) !== -1) {
synapse[header] = parseInt(synapse[header]);
}//if
});
results.synapses.push(synapse);
}
break;
case STATES.ABORT:
;
default:
self.abort("Invalid state while parsing import data. Check code.");
state = STATES.ABORT;
}
});
if (state === STATES.ABORT) {
return false;
} else {
return results;
}
},
importTopics: function(parsedTopics) {
var self = Metamaps.Import;
// up to 25 topics: scale 100
// up to 81 topics: scale 200
// up to 169 topics: scale 300
var scale = Math.floor((Math.sqrt(parsedTopics.length) - 1) / 4) * 100;
if (scale < 100) scale = 100;
var autoX = -scale;
var autoY = -scale;
parsedTopics.forEach(function(topic) {
var x, y;
if (topic.x && topic.y) {
x = topic.x;
y = topic.y;
} else {
x = autoX;
y = autoY;
autoX += 50;
if (autoX > scale) {
autoY += 50;
autoX = -scale;
}
}
self.createTopicWithParameters(
topic.name, topic.metacode, topic.permission,
topic.desc, topic.link, x, y, topic.id
);
});
},
importSynapses: function(parsedSynapses) {
var self = Metamaps.Import;
parsedSynapses.forEach(function(synapse) {
self.createSynapseWithParameters(
synapse.desc, synapse.category, synapse.permission,
synapse.topic1, synapse.topic2
);
});
},
createTopicWithParameters: function(name, metacode_name, permission, desc,
link, xloc, yloc, import_id) {
var self = Metamaps.Import;
$(document).trigger(Metamaps.Map.events.editedByActiveMapper);
var metacode = Metamaps.Metacodes.where({name: metacode_name})[0] || null;
if (metacode === null) return console.error("metacode not found");
var topic = new Metamaps.Backbone.Topic({
name: name,
metacode_id: metacode.id,
permission: permission || Metamaps.Active.Map.get('permission'),
desc: desc,
link: link,
});
Metamaps.Topics.add(topic);
self.cidMappings[import_id] = topic.cid;
var mapping = new Metamaps.Backbone.Mapping({
xloc: xloc,
yloc: yloc,
mappable_id: topic.cid,
mappable_type: "Topic",
});
Metamaps.Mappings.add(mapping);
// this function also includes the creation of the topic in the database
Metamaps.Topic.renderTopic(mapping, topic, true, true);
Metamaps.Famous.viz.hideInstructions();
},
createSynapseWithParameters: function(description, category, permission,
node1_id, node2_id) {
var self = Metamaps.Import;
var topic1 = Metamaps.Topics.get(self.cidMappings[node1_id]);
var topic2 = Metamaps.Topics.get(self.cidMappings[node2_id]);
var node1 = topic1.get('node');
var node2 = topic2.get('node');
// TODO check if topic1 and topic2 were sucessfully found...
var synapse = new Metamaps.Backbone.Synapse({
desc: description,
category: category,
permission: permission,
node1_id: node1.id,
node2_id: node2.id,
});
var mapping = new Metamaps.Backbone.Mapping({
mappable_type: "Synapse",
mappable_id: synapse.id,
});
Metamaps.Synapse.renderSynapse(mapping, synapse, node1, node2, true);
},
};