]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/read-package-json/node_modules/json-parse-helpfulerror/node_modules/jju/lib/document.js
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / node_modules / read-package-json / node_modules / json-parse-helpfulerror / node_modules / jju / lib / document.js
diff --git a/org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/read-package-json/node_modules/json-parse-helpfulerror/node_modules/jju/lib/document.js b/org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/read-package-json/node_modules/json-parse-helpfulerror/node_modules/jju/lib/document.js
new file mode 100644 (file)
index 0000000..cfab869
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Author: Alex Kocharin <alex@kocharin.ru>
+ * GIT: https://github.com/rlidwka/jju
+ * License: WTFPL, grab your copy here: http://www.wtfpl.net/txt/copying/
+ */
+
+var assert = require('assert')
+var tokenize = require('./parse').tokenize
+var stringify = require('./stringify').stringify
+var analyze = require('./analyze').analyze
+
+function isObject(x) {
+  return typeof(x) === 'object' && x !== null
+}
+
+function value_to_tokenlist(value, stack, options, is_key, indent) {
+  options = Object.create(options)
+  options._stringify_key = !!is_key
+
+  if (indent) {
+    options._prefix = indent.prefix.map(function(x) {
+      return x.raw
+    }).join('')
+  }
+
+  if (options._splitMin == null) options._splitMin = 0
+  if (options._splitMax == null) options._splitMax = 0
+
+  var stringified = stringify(value, options)
+
+  if (is_key) {
+    return [ { raw: stringified, type: 'key', stack: stack, value: value } ]
+  }
+
+  options._addstack = stack
+  var result = tokenize(stringified, {
+    _addstack: stack,
+  })
+  result.data = null
+  return result
+}
+
+// '1.2.3' -> ['1','2','3']
+function arg_to_path(path) {
+  // array indexes
+  if (typeof(path) === 'number') path = String(path)
+
+  if (path === '') path = []
+  if (typeof(path) === 'string') path = path.split('.')
+
+  if (!Array.isArray(path)) throw Error('Invalid path type, string or array expected')
+  return path
+}
+
+// returns new [begin, end] or false if not found
+//
+//          {x:3, xxx: 111, y: [111,  {q: 1, e: 2}  ,333]  }
+// f('y',0) returns this       B^^^^^^^^^^^^^^^^^^^^^^^^E
+// then f('1',1) would reduce it to   B^^^^^^^^^^E
+function find_element_in_tokenlist(element, lvl, tokens, begin, end) {
+  while(tokens[begin].stack[lvl] != element) {
+    if (begin++ >= end) return false
+  }
+  while(tokens[end].stack[lvl] != element) {
+    if (end-- < begin) return false
+  }
+  return [begin, end]
+}
+
+function is_whitespace(token_type) {
+  return token_type === 'whitespace'
+      || token_type === 'newline'
+      || token_type === 'comment'
+}
+
+function find_first_non_ws_token(tokens, begin, end) {
+  while(is_whitespace(tokens[begin].type)) {
+    if (begin++ >= end) return false
+  }
+  return begin
+}
+
+function find_last_non_ws_token(tokens, begin, end) {
+  while(is_whitespace(tokens[end].type)) {
+    if (end-- < begin) return false
+  }
+  return end
+}
+
+/*
+ * when appending a new element of an object/array, we are trying to
+ * figure out the style used on the previous element
+ *
+ * return {prefix, sep1, sep2, suffix}
+ *
+ *      '    "key" :  "element"    \r\n'
+ * prefix^^^^ sep1^ ^^sep2     ^^^^^^^^suffix
+ *
+ * begin - the beginning of the object/array
+ * end - last token of the last element (value or comma usually)
+ */
+function detect_indent_style(tokens, is_array, begin, end, level) {
+  var result = {
+    sep1: [],
+    sep2: [],
+    suffix: [],
+    prefix: [],
+    newline: [],
+  }
+
+  if (tokens[end].type === 'separator' && tokens[end].stack.length !== level+1 && tokens[end].raw !== ',') {
+    // either a beginning of the array (no last element) or other weird situation
+    //
+    // just return defaults
+    return result
+  }
+
+  //                              ' "key"  : "value"  ,'
+  // skipping last separator, we're now here        ^^
+  if (tokens[end].type === 'separator')
+    end = find_last_non_ws_token(tokens, begin, end - 1)
+  if (end === false) return result
+
+  //                              ' "key"  : "value"  ,'
+  // skipping value                          ^^^^^^^
+  while(tokens[end].stack.length > level) end--
+
+  if (!is_array) {
+    while(is_whitespace(tokens[end].type)) {
+      if (end < begin) return result
+      if (tokens[end].type === 'whitespace') {
+        result.sep2.unshift(tokens[end])
+      } else {
+        // newline, comment or other unrecognized codestyle
+        return result
+      }
+      end--
+    }
+
+    //                              ' "key"  : "value"  ,'
+    // skipping separator                    ^
+    assert.equal(tokens[end].type, 'separator')
+    assert.equal(tokens[end].raw, ':')
+    while(is_whitespace(tokens[--end].type)) {
+      if (end < begin) return result
+      if (tokens[end].type === 'whitespace') {
+        result.sep1.unshift(tokens[end])
+      } else {
+        // newline, comment or other unrecognized codestyle
+        return result
+      }
+    }
+
+    assert.equal(tokens[end].type, 'key')
+    end--
+  }
+
+  //                              ' "key"  : "value"  ,'
+  // skipping key                   ^^^^^
+  while(is_whitespace(tokens[end].type)) {
+    if (end < begin) return result
+    if (tokens[end].type === 'whitespace') {
+      result.prefix.unshift(tokens[end])
+    } else if (tokens[end].type === 'newline') {
+      result.newline.unshift(tokens[end])
+      return result
+    } else {
+      // comment or other unrecognized codestyle
+      return result
+    }
+    end--
+  }
+
+  return result
+}
+
+function Document(text, options) {
+  var self = Object.create(Document.prototype)
+
+  if (options == null) options = {}
+  //options._structure = true
+  var tokens = self._tokens = tokenize(text, options)
+  self._data = tokens.data
+  tokens.data = null
+  self._options = options
+
+  var stats = analyze(text, options)
+  if (options.indent == null) {
+    options.indent = stats.indent
+  }
+  if (options.quote == null) {
+    options.quote = stats.quote
+  }
+  if (options.quote_keys == null) {
+    options.quote_keys = stats.quote_keys
+  }
+  if (options.no_trailing_comma == null) {
+    options.no_trailing_comma = !stats.has_trailing_comma
+  }
+  return self
+}
+
+// return true if it's a proper object
+//        throw otherwise
+function check_if_can_be_placed(key, object, is_unset) {
+  //if (object == null) return false
+  function error(add) {
+    return Error("You can't " + (is_unset ? 'unset' : 'set') + " key '" + key + "'" + add)
+  }
+
+  if (!isObject(object)) {
+    throw error(' of an non-object')
+  }
+  if (Array.isArray(object)) {
+    // array, check boundary
+    if (String(key).match(/^\d+$/)) {
+      key = Number(String(key))
+      if (object.length < key || (is_unset && object.length === key)) {
+        throw error(', out of bounds')
+      } else if (is_unset && object.length !== key+1) {
+        throw error(' in the middle of an array')
+      } else {
+        return true
+      }
+    } else {
+      throw error(' of an array')
+    }
+  } else {
+    // object
+    return true
+  }
+}
+
+// usage: document.set('path.to.something', 'value')
+//    or: document.set(['path','to','something'], 'value')
+Document.prototype.set = function(path, value) {
+  path = arg_to_path(path)
+
+  // updating this._data and check for errors
+  if (path.length === 0) {
+    if (value === undefined) throw Error("can't remove root document")
+    this._data = value
+    var new_key = false
+
+  } else {
+    var data = this._data
+
+    for (var i=0; i<path.length-1; i++) {
+      check_if_can_be_placed(path[i], data, false)
+      data = data[path[i]]
+    }
+    if (i === path.length-1) {
+      check_if_can_be_placed(path[i], data, value === undefined)
+    }
+
+    var new_key = !(path[i] in data)
+
+    if (value === undefined) {
+      if (Array.isArray(data)) {
+        data.pop()
+      } else {
+        delete data[path[i]]
+      }
+    } else {
+      data[path[i]] = value
+    }
+  }
+
+  // for inserting document
+  if (!this._tokens.length)
+    this._tokens = [ { raw: '', type: 'literal', stack: [], value: undefined } ]
+
+  var position = [
+    find_first_non_ws_token(this._tokens, 0, this._tokens.length - 1),
+    find_last_non_ws_token(this._tokens, 0, this._tokens.length - 1),
+  ]
+  for (var i=0; i<path.length-1; i++) {
+    position = find_element_in_tokenlist(path[i], i, this._tokens, position[0], position[1])
+    if (position == false) throw Error('internal error, please report this')
+  }
+  // assume that i == path.length-1 here
+
+  if (path.length === 0) {
+    var newtokens = value_to_tokenlist(value, path, this._options)
+    // all good
+
+  } else if (!new_key) {
+    // replace old value with a new one (or deleting something)
+    var pos_old = position
+    position = find_element_in_tokenlist(path[i], i, this._tokens, position[0], position[1])
+
+    if (value === undefined && position !== false) {
+      // deleting element (position !== false ensures there's something)
+      var newtokens = []
+
+      if (!Array.isArray(data)) {
+        // removing element from an object, `{x:1, key:CURRENT} -> {x:1}`
+        // removing sep, literal and optional sep
+        // ':'
+        var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1)
+        assert.equal(this._tokens[pos2].type, 'separator')
+        assert.equal(this._tokens[pos2].raw, ':')
+        position[0] = pos2
+
+        // key
+        var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1)
+        assert.equal(this._tokens[pos2].type, 'key')
+        assert.equal(this._tokens[pos2].value, path[path.length-1])
+        position[0] = pos2
+      }
+
+      // removing comma in arrays and objects
+      var pos2 = find_last_non_ws_token(this._tokens, pos_old[0], position[0] - 1)
+      assert.equal(this._tokens[pos2].type, 'separator')
+      if (this._tokens[pos2].raw === ',') {
+        position[0] = pos2
+      } else {
+        // beginning of the array/object, so we should remove trailing comma instead
+        pos2 = find_first_non_ws_token(this._tokens, position[1] + 1, pos_old[1])
+        assert.equal(this._tokens[pos2].type, 'separator')
+        if (this._tokens[pos2].raw === ',') {
+          position[1] = pos2
+        }
+      }
+
+    } else {
+      var indent = pos2 !== false
+                 ? detect_indent_style(this._tokens, Array.isArray(data), pos_old[0], position[1] - 1, i)
+                 : {}
+      var newtokens = value_to_tokenlist(value, path, this._options, false, indent)
+    }
+
+  } else {
+    // insert new key, that's tricky
+    var path_1 = path.slice(0, i)
+
+    //  find a last separator after which we're inserting it
+    var pos2 = find_last_non_ws_token(this._tokens, position[0] + 1, position[1] - 1)
+    assert(pos2 !== false)
+
+    var indent = pos2 !== false
+               ? detect_indent_style(this._tokens, Array.isArray(data), position[0] + 1, pos2, i)
+               : {}
+
+    var newtokens = value_to_tokenlist(value, path, this._options, false, indent)
+
+    // adding leading whitespaces according to detected codestyle
+    var prefix = []
+    if (indent.newline && indent.newline.length)
+      prefix = prefix.concat(indent.newline)
+    if (indent.prefix && indent.prefix.length)
+      prefix = prefix.concat(indent.prefix)
+
+    // adding '"key":' (as in "key":"value") to object values
+    if (!Array.isArray(data)) {
+      prefix = prefix.concat(value_to_tokenlist(path[path.length-1], path_1, this._options, true))
+      if (indent.sep1 && indent.sep1.length)
+        prefix = prefix.concat(indent.sep1)
+      prefix.push({raw: ':', type: 'separator', stack: path_1})
+      if (indent.sep2 && indent.sep2.length)
+        prefix = prefix.concat(indent.sep2)
+    }
+
+    newtokens.unshift.apply(newtokens, prefix)
+
+    // check if prev token is a separator AND they're at the same level
+    if (this._tokens[pos2].type === 'separator' && this._tokens[pos2].stack.length === path.length-1) {
+      // previous token is either , or [ or {
+      if (this._tokens[pos2].raw === ',') {
+        // restore ending comma
+        newtokens.push({raw: ',', type: 'separator', stack: path_1})
+      }
+    } else {
+      // previous token isn't a separator, so need to insert one
+      newtokens.unshift({raw: ',', type: 'separator', stack: path_1})
+    }
+
+    if (indent.suffix && indent.suffix.length)
+      newtokens.push.apply(newtokens, indent.suffix)
+
+    assert.equal(this._tokens[position[1]].type, 'separator')
+    position[0] = pos2+1
+    position[1] = pos2
+  }
+
+  newtokens.unshift(position[1] - position[0] + 1)
+  newtokens.unshift(position[0])
+  this._tokens.splice.apply(this._tokens, newtokens)
+
+  return this
+}
+
+// convenience method
+Document.prototype.unset = function(path) {
+  return this.set(path, undefined)
+}
+
+Document.prototype.get = function(path) {
+  path = arg_to_path(path)
+
+  var data = this._data
+  for (var i=0; i<path.length; i++) {
+    if (!isObject(data)) return undefined
+    data = data[path[i]]
+  }
+  return data
+}
+
+Document.prototype.has = function(path) {
+  path = arg_to_path(path)
+
+  var data = this._data
+  for (var i=0; i<path.length; i++) {
+    if (!isObject(data)) return false
+    data = data[path[i]]
+  }
+  return data !== undefined
+}
+
+// compare old object and new one, and change differences only
+Document.prototype.update = function(value) {
+  var self = this
+  change([], self._data, value)
+  return self
+
+  function change(path, old_data, new_data) {
+    if (!isObject(new_data) || !isObject(old_data)) {
+      // if source or dest is primitive, just replace
+      if (new_data !== old_data)
+        self.set(path, new_data)
+
+    } else if (Array.isArray(new_data) != Array.isArray(old_data)) {
+      // old data is an array XOR new data is an array, replace as well
+      self.set(path, new_data)
+
+    } else if (Array.isArray(new_data)) {
+      // both values are arrays here
+
+      if (new_data.length > old_data.length) {
+        // adding new elements, so going forward
+        for (var i=0; i<new_data.length; i++) {
+          path.push(String(i))
+          change(path, old_data[i], new_data[i])
+          path.pop()
+        }
+
+      } else {
+        // removing something, so going backward
+        for (var i=old_data.length-1; i>=0; i--) {
+          path.push(String(i))
+          change(path, old_data[i], new_data[i])
+          path.pop()
+        }
+      }
+
+    } else {
+      // both values are objects here
+      for (var i in new_data) {
+        path.push(String(i))
+        change(path, old_data[i], new_data[i])
+        path.pop()
+      }
+
+      for (var i in old_data) {
+        if (i in new_data) continue
+        path.push(String(i))
+        change(path, old_data[i], new_data[i])
+        path.pop()
+      }
+    }
+  }
+}
+
+Document.prototype.toString = function() {
+  return this._tokens.map(function(x) {
+    return x.raw
+  }).join('')
+}
+
+module.exports.Document = Document
+
+module.exports.update = function updateJSON(source, new_value, options) {
+  return Document(source, options).update(new_value).toString()
+}
+