From 2b488b5567680fb5ed502ed7c024e2a7219bdf2e Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Tue, 14 Jun 2016 22:25:11 +0200 Subject: [PATCH] refactor "util" into ES6 modules for #3118 --- Makefile | 9 +- index.html | 5 +- js/lib/id/util.js | 272 +++++++++++++++++++++++ modules/util/index.js | 18 ++ {js/id => modules}/util/session_mutex.js | 4 +- {js/id => modules}/util/suggest_names.js | 4 +- {js/id => modules/util}/util.js | 69 +++--- test/index.html | 18 ++ 8 files changed, 352 insertions(+), 47 deletions(-) create mode 100644 js/lib/id/util.js create mode 100644 modules/util/index.js rename {js/id => modules}/util/session_mutex.js (95%) rename {js/id => modules}/util/suggest_names.js (93%) rename {js/id => modules/util}/util.js (82%) diff --git a/Makefile b/Makefile index 1f681c269..3a7f865dc 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,8 @@ $(BUILDJS_TARGETS): $(BUILDJS_SOURCES) build.js MODULE_TARGETS = \ js/lib/id/actions.js \ js/lib/id/presets.js \ - js/lib/id/validations.js + js/lib/id/validations.js \ + js/lib/id/util.js js/lib/id/actions.js: modules/ node_modules/.bin/rollup -f umd -n iD.actions modules/actions/index.js --no-strict > $@ @@ -56,6 +57,9 @@ js/lib/id/presets.js: modules/ js/lib/id/validations.js: modules/ node_modules/.bin/rollup -f umd -n iD.validations modules/validations/index.js --no-strict > $@ +js/lib/id/util.js: modules/ + node_modules/.bin/rollup -f umd -n iD.util modules/util/index.js --no-strict > $@ + dist/iD.js: \ js/lib/bootstrap-tooltip.js \ js/lib/d3.v3.js \ @@ -85,9 +89,6 @@ dist/iD.js: \ js/id/services/taginfo.js \ js/id/services/wikidata.js \ js/id/services/wikipedia.js \ - js/id/util.js \ - js/id/util/session_mutex.js \ - js/id/util/suggest_names.js \ js/id/geo.js \ js/id/geo/extent.js \ js/id/geo/intersection.js \ diff --git a/index.html b/index.html index 4473a9e5b..77c775929 100644 --- a/index.html +++ b/index.html @@ -36,10 +36,7 @@ - - - - + diff --git a/js/lib/id/util.js b/js/lib/id/util.js new file mode 100644 index 000000000..01c2ffdb0 --- /dev/null +++ b/js/lib/id/util.js @@ -0,0 +1,272 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.iD = global.iD || {}, global.iD.util = global.iD.util || {}))); +}(this, function (exports) { 'use strict'; + + function tagText(entity) { + return d3.entries(entity.tags).map(function(e) { + return e.key + '=' + e.value; + }).join(', '); + } + + function entitySelector(ids) { + return ids.length ? '.' + ids.join(',.') : 'nothing'; + } + + function entityOrMemberSelector(ids, graph) { + var s = entitySelector(ids); + + ids.forEach(function(id) { + var entity = graph.hasEntity(id); + if (entity && entity.type === 'relation') { + entity.members.forEach(function(member) { + s += ',.' + member.id; + }); + } + }); + + return s; + } + + function displayName(entity) { + var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0]; + return entity.tags[localeName] || entity.tags.name || entity.tags.ref; + } + + function displayType(id) { + return { + n: t('inspector.node'), + w: t('inspector.way'), + r: t('inspector.relation') + }[id.charAt(0)]; + } + + function stringQs(str) { + return str.split('&').reduce(function(obj, pair){ + var parts = pair.split('='); + if (parts.length === 2) { + obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]); + } + return obj; + }, {}); + } + + function qsString(obj, noencode) { + function softEncode(s) { + // encode everything except special characters used in certain hash parameters: + // "/" in map states, ":", ",", {" and "}" in background + return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent); + } + return Object.keys(obj).sort().map(function(key) { + return encodeURIComponent(key) + '=' + ( + noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key])); + }).join('&'); + } + + function prefixDOMProperty(property) { + var prefixes = ['webkit', 'ms', 'moz', 'o'], + i = -1, + n = prefixes.length, + s = document.body; + + if (property in s) + return property; + + property = property.substr(0, 1).toUpperCase() + property.substr(1); + + while (++i < n) + if (prefixes[i] + property in s) + return prefixes[i] + property; + + return false; + } + + function prefixCSSProperty(property) { + var prefixes = ['webkit', 'ms', 'Moz', 'O'], + i = -1, + n = prefixes.length, + s = document.body.style; + + if (property.toLowerCase() in s) + return property.toLowerCase(); + + while (++i < n) + if (prefixes[i] + property in s) + return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase(); + + return false; + } + + + var transformProperty; + function setTransform(el, x, y, scale) { + var prop = transformProperty = transformProperty || prefixCSSProperty('Transform'), + translate = iD.detect().opera ? + 'translate(' + x + 'px,' + y + 'px)' : + 'translate3d(' + x + 'px,' + y + 'px,0)'; + return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : '')); + } + + function getStyle(selector) { + for (var i = 0; i < document.styleSheets.length; i++) { + var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || []; + for (var k = 0; k < rules.length; k++) { + var selectorText = rules[k].selectorText && rules[k].selectorText.split(', '); + if (_.includes(selectorText, selector)) { + return rules[k]; + } + } + } + } + + function editDistance(a, b) { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + var matrix = []; + for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; } + for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; } + for (i = 1; i <= b.length; i++) { + for (j = 1; j <= a.length; j++) { + if (b.charAt(i-1) === a.charAt(j-1)) { + matrix[i][j] = matrix[i-1][j-1]; + } else { + matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution + Math.min(matrix[i][j-1] + 1, // insertion + matrix[i-1][j] + 1)); // deletion + } + } + } + return matrix[b.length][a.length]; + } + + // a d3.mouse-alike which + // 1. Only works on HTML elements, not SVG + // 2. Does not cause style recalculation + function fastMouse(container) { + var rect = container.getBoundingClientRect(), + rectLeft = rect.left, + rectTop = rect.top, + clientLeft = +container.clientLeft, + clientTop = +container.clientTop; + return function(e) { + return [ + e.clientX - rectLeft - clientLeft, + e.clientY - rectTop - clientTop]; + }; + } + + /* eslint-disable no-proto */ + const getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; }; + /* eslint-enable no-proto */ + + function asyncMap(inputs, func, callback) { + var remaining = inputs.length, + results = [], + errors = []; + + inputs.forEach(function(d, i) { + func(d, function done(err, data) { + errors[i] = err; + results[i] = data; + remaining--; + if (!remaining) callback(errors, results); + }); + }); + } + + // wraps an index to an interval [0..length-1] + function wrap(index, length) { + if (index < 0) + index += Math.ceil(-index/length)*length; + return index % length; + } + + // A per-domain session mutex backed by a cookie and dead man's + // switch. If the session crashes, the mutex will auto-release + // after 5 seconds. + + function SessionMutex(name) { + var mutex = {}, + intervalID; + + function renew() { + var expires = new Date(); + expires.setSeconds(expires.getSeconds() + 5); + document.cookie = name + '=1; expires=' + expires.toUTCString(); + } + + mutex.lock = function() { + if (intervalID) return true; + var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); + if (cookie) return false; + renew(); + intervalID = window.setInterval(renew, 4000); + return true; + }; + + mutex.unlock = function() { + if (!intervalID) return; + document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + clearInterval(intervalID); + intervalID = null; + }; + + mutex.locked = function() { + return !!intervalID; + }; + + return mutex; + } + + function SuggestNames(preset, suggestions) { + preset = preset.id.split('/', 2); + var k = preset[0], + v = preset[1]; + + return function(value, callback) { + var result = []; + if (value && value.length > 2) { + if (suggestions[k] && suggestions[k][v]) { + for (var sugg in suggestions[k][v]) { + var dist = iD.util.editDistance(value, sugg.substring(0, value.length)); + if (dist < 3) { + result.push({ + title: sugg, + value: sugg, + dist: dist + }); + } + } + } + result.sort(function(a, b) { + return a.dist - b.dist; + }); + } + result = result.slice(0,3); + callback(result); + }; + } + + exports.tagText = tagText; + exports.entitySelector = entitySelector; + exports.entityOrMemberSelector = entityOrMemberSelector; + exports.displayName = displayName; + exports.displayType = displayType; + exports.stringQs = stringQs; + exports.qsString = qsString; + exports.prefixDOMProperty = prefixDOMProperty; + exports.prefixCSSProperty = prefixCSSProperty; + exports.setTransform = setTransform; + exports.getStyle = getStyle; + exports.editDistance = editDistance; + exports.fastMouse = fastMouse; + exports.getPrototypeOf = getPrototypeOf; + exports.asyncMap = asyncMap; + exports.wrap = wrap; + exports.SessionMutex = SessionMutex; + exports.SuggestNames = SuggestNames; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); \ No newline at end of file diff --git a/modules/util/index.js b/modules/util/index.js new file mode 100644 index 000000000..92780dffa --- /dev/null +++ b/modules/util/index.js @@ -0,0 +1,18 @@ +export { tagText } from './util'; +export { entitySelector } from './util'; +export { entityOrMemberSelector } from './util'; +export { displayName } from './util'; +export { displayType } from './util'; +export { stringQs } from './util'; +export { qsString } from './util'; +export { prefixDOMProperty } from './util'; +export { prefixCSSProperty } from './util'; +export { setTransform } from './util'; +export { getStyle } from './util'; +export { editDistance } from './util'; +export { fastMouse } from './util'; +export { getPrototypeOf } from './util'; +export { asyncMap } from './util'; +export { wrap } from './util'; +export { SessionMutex } from './session_mutex'; +export { SuggestNames } from './suggest_names'; diff --git a/js/id/util/session_mutex.js b/modules/util/session_mutex.js similarity index 95% rename from js/id/util/session_mutex.js rename to modules/util/session_mutex.js index da9a195c3..f71a6e413 100644 --- a/js/id/util/session_mutex.js +++ b/modules/util/session_mutex.js @@ -2,7 +2,7 @@ // switch. If the session crashes, the mutex will auto-release // after 5 seconds. -iD.util.SessionMutex = function(name) { +export function SessionMutex(name) { var mutex = {}, intervalID; @@ -33,4 +33,4 @@ iD.util.SessionMutex = function(name) { }; return mutex; -}; +} diff --git a/js/id/util/suggest_names.js b/modules/util/suggest_names.js similarity index 93% rename from js/id/util/suggest_names.js rename to modules/util/suggest_names.js index 79040c003..a667c3a7f 100644 --- a/js/id/util/suggest_names.js +++ b/modules/util/suggest_names.js @@ -1,4 +1,4 @@ -iD.util.SuggestNames = function(preset, suggestions) { +export function SuggestNames(preset, suggestions) { preset = preset.id.split('/', 2); var k = preset[0], v = preset[1]; @@ -25,4 +25,4 @@ iD.util.SuggestNames = function(preset, suggestions) { result = result.slice(0,3); callback(result); }; -}; +} diff --git a/js/id/util.js b/modules/util/util.js similarity index 82% rename from js/id/util.js rename to modules/util/util.js index 050355c84..a7020d7a5 100644 --- a/js/id/util.js +++ b/modules/util/util.js @@ -1,17 +1,15 @@ -iD.util = {}; - -iD.util.tagText = function(entity) { +export function tagText(entity) { return d3.entries(entity.tags).map(function(e) { return e.key + '=' + e.value; }).join(', '); -}; +} -iD.util.entitySelector = function(ids) { +export function entitySelector(ids) { return ids.length ? '.' + ids.join(',.') : 'nothing'; -}; +} -iD.util.entityOrMemberSelector = function(ids, graph) { - var s = iD.util.entitySelector(ids); +export function entityOrMemberSelector(ids, graph) { + var s = entitySelector(ids); ids.forEach(function(id) { var entity = graph.hasEntity(id); @@ -23,22 +21,22 @@ iD.util.entityOrMemberSelector = function(ids, graph) { }); return s; -}; +} -iD.util.displayName = function(entity) { +export function displayName(entity) { var localeName = 'name:' + iD.detect().locale.toLowerCase().split('-')[0]; return entity.tags[localeName] || entity.tags.name || entity.tags.ref; -}; +} -iD.util.displayType = function(id) { +export function displayType(id) { return { n: t('inspector.node'), w: t('inspector.way'), r: t('inspector.relation') }[id.charAt(0)]; -}; +} -iD.util.stringQs = function(str) { +export function stringQs(str) { return str.split('&').reduce(function(obj, pair){ var parts = pair.split('='); if (parts.length === 2) { @@ -46,9 +44,9 @@ iD.util.stringQs = function(str) { } return obj; }, {}); -}; +} -iD.util.qsString = function(obj, noencode) { +export function qsString(obj, noencode) { function softEncode(s) { // encode everything except special characters used in certain hash parameters: // "/" in map states, ":", ",", {" and "}" in background @@ -58,9 +56,9 @@ iD.util.qsString = function(obj, noencode) { return encodeURIComponent(key) + '=' + ( noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key])); }).join('&'); -}; +} -iD.util.prefixDOMProperty = function(property) { +export function prefixDOMProperty(property) { var prefixes = ['webkit', 'ms', 'moz', 'o'], i = -1, n = prefixes.length, @@ -76,9 +74,9 @@ iD.util.prefixDOMProperty = function(property) { return prefixes[i] + property; return false; -}; +} -iD.util.prefixCSSProperty = function(property) { +export function prefixCSSProperty(property) { var prefixes = ['webkit', 'ms', 'Moz', 'O'], i = -1, n = prefixes.length, @@ -92,18 +90,19 @@ iD.util.prefixCSSProperty = function(property) { return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase(); return false; -}; +} -iD.util.setTransform = function(el, x, y, scale) { - var prop = iD.util.transformProperty = iD.util.transformProperty || iD.util.prefixCSSProperty('Transform'), +var transformProperty; +export function setTransform(el, x, y, scale) { + var prop = transformProperty = transformProperty || prefixCSSProperty('Transform'), translate = iD.detect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)'; return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : '')); -}; +} -iD.util.getStyle = function(selector) { +export function getStyle(selector) { for (var i = 0; i < document.styleSheets.length; i++) { var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules || []; for (var k = 0; k < rules.length; k++) { @@ -113,9 +112,9 @@ iD.util.getStyle = function(selector) { } } } -}; +} -iD.util.editDistance = function(a, b) { +export function editDistance(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; var matrix = []; @@ -133,12 +132,12 @@ iD.util.editDistance = function(a, b) { } } return matrix[b.length][a.length]; -}; +} // a d3.mouse-alike which // 1. Only works on HTML elements, not SVG // 2. Does not cause style recalculation -iD.util.fastMouse = function(container) { +export function fastMouse(container) { var rect = container.getBoundingClientRect(), rectLeft = rect.left, rectTop = rect.top, @@ -149,13 +148,13 @@ iD.util.fastMouse = function(container) { e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop]; }; -}; +} /* eslint-disable no-proto */ -iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; }; +export const getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; }; /* eslint-enable no-proto */ -iD.util.asyncMap = function(inputs, func, callback) { +export function asyncMap(inputs, func, callback) { var remaining = inputs.length, results = [], errors = []; @@ -168,11 +167,11 @@ iD.util.asyncMap = function(inputs, func, callback) { if (!remaining) callback(errors, results); }); }); -}; +} // wraps an index to an interval [0..length-1] -iD.util.wrap = function(index, length) { +export function wrap(index, length) { if (index < 0) index += Math.ceil(-index/length)*length; return index % length; -}; +} diff --git a/test/index.html b/test/index.html index 80564fdfb..00226e2c6 100644 --- a/test/index.html +++ b/test/index.html @@ -42,10 +42,14 @@ +<<<<<<< 6c7786ab274f46879f15cbb3ba2016a540cf8df5 +======= + +>>>>>>> refactor "util" into ES6 modules for #3118 @@ -186,8 +190,22 @@ +<<<<<<< 6c7786ab274f46879f15cbb3ba2016a540cf8df5 +======= + + + + + + + + + + + +>>>>>>> refactor "util" into ES6 modules for #3118