diff --git a/frontend/src/Metamaps/AutoLayout.js b/frontend/src/Metamaps/AutoLayout.js index ee9dc33c..f3e91440 100644 --- a/frontend/src/Metamaps/AutoLayout.js +++ b/frontend/src/Metamaps/AutoLayout.js @@ -7,7 +7,7 @@ const AutoLayout = { nextYshift: 0, timeToTurn: 0, - getNextCoord: function () { + getNextCoord: function (opts = {}) { var self = AutoLayout var nextX = self.nextX var nextY = self.nextY @@ -49,9 +49,22 @@ const AutoLayout = { } } - return { - x: nextX, - y: nextY + if (opts.map && self.coordsTaken(nextX, nextY, opts.map)) { + // check if the coordinate is already taken on the current map + return self.getNextCoord(opts) + } else { + return { + x: nextX, + y: nextY + } + } + }, + coordsTaken: function (x, y, map) { + const mappings = map.getMappings() + if (mappings.findWhere({ xloc: x, yloc: y })) { + return true + } else { + return false } }, resetSpiral: function () { diff --git a/frontend/src/Metamaps/Import.js b/frontend/src/Metamaps/Import.js index 1307aa45..5d5f91a7 100644 --- a/frontend/src/Metamaps/Import.js +++ b/frontend/src/Metamaps/Import.js @@ -4,6 +4,7 @@ import parse from 'csv-parse' import _ from 'lodash' import Active from './Active' +import AutoLayout from './AutoLayout' import GlobalUI from './GlobalUI' import Map from './Map' import Synapse from './Synapse' @@ -40,9 +41,9 @@ const Import = { const topicsRegex = /("?Topics"?)([\s\S]*)/mi const synapsesRegex = /("?Synapses"?)([\s\S]*)/mi - let topicsText = text.match(topicsRegex) + let topicsText = text.match(topicsRegex) || '' if (topicsText) topicsText = topicsText[2].replace(synapsesRegex, '') - let synapsesText = text.match(synapsesRegex) + let synapsesText = text.match(synapsesRegex) || '' if (synapsesText) synapsesText = synapsesText[2].replace(topicsRegex, '') // merge default options and extra options passed in parserOpts argument @@ -54,14 +55,20 @@ const Import = { const topicsPromise = $.Deferred() parse(topicsText, csv_parser_options, (err, data) => { - if (err) return topicsPromise.reject(err) - topicsPromise.resolve(data.map(row => self.lowercaseKeys(row))) + if (err) { + console.warn(err) + return topicsPromise.resolve([]) + } + topicsPromise.resolve(data.map(row => self.normalizeKeys(row))) }) const synapsesPromise = $.Deferred() parse(synapsesText, csv_parser_options, (err, data) => { - if (err) return synapsesPromise.reject(err) - synapsesPromise.resolve(data.map(row => self.lowercaseKeys(row))) + if (err) { + console.warn(err) + return synapsesPromise.resolve([]) + } + synapsesPromise.resolve(data.map(row => self.normalizeKeys(row))) }) $.when(topicsPromise, synapsesPromise).done((topics, synapses) => { @@ -217,32 +224,26 @@ const Import = { importTopics: function (parsedTopics) { var self = 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 - } + let coords = { x: topic.x, y: topic.y } + if (!coords.x || !coords.y) { + coords = AutoLayout.getNextCoord({ map: Active.Map }) + } + + if (!topic.name && topic.link || + topic.name && topic.link && !topic.metacode) { + self.handleURL(topic.link, { + coords, + name: topic.name, + permission: topic.permission, + import_id: topic.id + }) + return // "continue" } self.createTopicWithParameters( topic.name, topic.metacode, topic.permission, - topic.desc, topic.link, x, y, topic.id + topic.desc, topic.link, coords.x, coords.y, topic.id ) }) }, @@ -345,6 +346,47 @@ const Import = { Synapse.renderSynapse(mapping, synapse, node1, node2, true) }, + handleURL: function (url, opts = {}) { + let coords = opts.coords + if (!coords || coords.x === undefined || coords.y === undefined) { + coords = AutoLayout.getNextCoord({ map: Active.Map }) + } + + const name = opts.name || 'Link' + const metacode = opts.metacode || 'Reference' + const import_id = opts.import_id || null // don't store a cidMapping + const permission = opts.permission || null // use default + const desc = opts.desc || url + + Import.createTopicWithParameters( + name, + metacode, + permission, + desc, + url, + coords.x, + coords.y, + import_id, + { + success: function(topic) { + if (topic.get('name') !== 'Link') return + $.get('/hacks/load_url_title', { + url + }, function success(data, textStatus) { + var selector = '#showcard #topic_' + topic.get('id') + ' .best_in_place' + if ($(selector).find('form').length > 0) { + $(selector).find('textarea, input').val(data.title) + } else { + $(selector).html(data.title) + } + topic.set('name', data.title) + topic.save() + }) + } + } + ) + }, + /* * helper functions */ @@ -353,6 +395,7 @@ const Import = { console.error(message) }, + // TODO investigate replacing with es6 (?) trim() simplify: function (string) { return string .replace(/(^\s*|\s*$)/g, '') @@ -361,9 +404,13 @@ const Import = { // thanks to http://stackoverflow.com/a/25290114/5332286 - lowercaseKeys: function(obj) { + normalizeKeys: function(obj) { return _.transform(obj, (result, val, key) => { - result[key.toLowerCase()] = val + let newKey = key.toLowerCase() + if (newKey === 'url') key = 'link' + if (newKey === 'title') key = 'name' + if (newKey === 'description') key = 'desc' + result[newKey] = val }) } } diff --git a/frontend/src/Metamaps/PasteInput.js b/frontend/src/Metamaps/PasteInput.js index e7029d66..bc20ec43 100644 --- a/frontend/src/Metamaps/PasteInput.js +++ b/frontend/src/Metamaps/PasteInput.js @@ -1,8 +1,6 @@ /* global $ */ -import AutoLayout from './AutoLayout' import Import from './Import' -import TopicCard from './TopicCard' import Util from './Util' const PasteInput = { @@ -58,7 +56,7 @@ const PasteInput = { var self = PasteInput if (text.match(self.URL_REGEX)) { - self.handleURL(text, coords) + Import.handleURL(text, coords) } else if (text[0] === '{') { Import.handleJSON(text) } else if (text.match(/\t/)) { @@ -67,47 +65,6 @@ const PasteInput = { // just try to see if CSV works Import.handleCSV(text) } - }, - - handleURL: function (text, coords) { - var title = 'Link' - if (!coords || !coords.x || !coords.y) { - coords = AutoLayout.getNextCoord() - } - - var import_id = null // don't store a cidMapping - var permission = null // use default - - Import.createTopicWithParameters( - title, - 'Reference', // metacode - todo fix - permission, - text, // desc - todo load from url? - text, // link - todo fix because this isn't being POSTed - coords.x, - coords.y, - import_id, - { - success: function(topic) { - $.get('/hacks/load_url_title', { - url: text - }, function success(data, textStatus) { - var selector = '#showcard #topic_' + topic.get('id') + ' .best_in_place' - if ($(selector).find('form').length > 0) { - $(selector).find('textarea, input').val(data.title) - } else { - $(selector).html(data.title) - } - topic.set('name', data.title) - topic.save() - }) - TopicCard.showCard(topic.get('node'), function() { - $('#showcard #titleActivator').click() - .find('textarea, input').focus() - }) - } - } - ) } } diff --git a/webpack.config.js b/webpack.config.js index f94f904a..91498abd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -26,7 +26,7 @@ const config = module.exports = { test: /\.(js|jsx)?$/, exclude: /node_modules/, loaders: [ - "babel-loader?cacheDirectory" + 'babel-loader?cacheDirectory&retainLines=true' ] } ] @@ -36,6 +36,7 @@ const config = module.exports = { }, output: { path: './app/assets/javascripts/webpacked', - filename: '[name].js' + filename: '[name].js', + devtoolModuleFilenameTemplate: '[absolute-resource-path]' } }