json-query
Version:
Retrieves values from JSON objects for data binding. Offers params, nested queries, deep queries, custom reduce/filter functions and simple boolean logic. Browserify compatible.
190 lines (156 loc) • 4.42 kB
JavaScript
module.exports = State
function State(options, params, handleQuery){
options = options || {}
//this.options = options
this.handleQuery = handleQuery
this.options = options
this.locals = this.options.locals || {}
this.globals = this.options.globals || {}
this.rootContext = firstNonNull(options.data, options.rootContext, options.context, options.source)
this.parent = options.parent
this.override = options.override
this.filters = options.filters || {}
this.params = params || options.params || []
this.context = firstNonNull(options.currentItem, options.context, options.source)
this.currentItem = firstNonNull(this.context, options.rootContext, options.data)
this.currentKey = null
this.currentReferences = []
this.currentParents = []
}
State.prototype = {
// current manipulation
setCurrent: function(key, value){
if (this.currentItem || this.currentKey || this.currentParents.length>0){
this.currentParents.push({key: this.currentKey, value: this.currentItem})
}
this.currentItem = value
this.currentKey = key
},
resetCurrent: function(){
this.currentItem = null
this.currentKey = null
this.currentParents = []
},
force: function(def){
var parent = this.currentParents[this.currentParents.length-1]
if (!this.currentItem && parent && (this.currentKey != null)){
this.currentItem = def || {}
parent.value[this.currentKey] = this.currentItem
}
return !!this.currentItem
},
getLocal: function(localName){
if (~localName.indexOf('/')){
var result = null
var parts = localName.split('/')
for (var i=0;i<parts.length;i++){
var part = parts[i]
if (i == 0){
result = this.locals[part]
} else if (result && result[part]){
result = result[part]
}
}
return result
} else {
return this.locals[localName]
}
},
getGlobal: function(globalName){
if (~globalName.indexOf('/')){
var result = null
var parts = globalName.split('/')
for (var i=0;i<parts.length;i++){
var part = parts[i]
if (i == 0){
result = this.globals[part]
} else if (result && result[part]){
result = result[part]
}
}
return result
} else {
return this.globals[globalName]
}
},
getFilter: function(filterName){
if (~filterName.indexOf('/')){
var result = null
var filterParts = filterName.split('/')
for (var i=0;i<filterParts.length;i++){
var part = filterParts[i]
if (i == 0){
result = this.filters[part]
} else if (result && result[part]){
result = result[part]
}
}
return result
} else {
return this.filters[filterName]
}
},
addReferences: function(references){
if (references){
references.forEach(this.addReference, this)
}
},
addReference: function(ref){
if (ref instanceof Object && !~this.currentReferences.indexOf(ref)){
this.currentReferences.push(ref)
}
},
// helper functions
getValues: function(values, callback){
return values.map(this.getValue, this)
},
getValue: function (value) {
return this.getValueFrom(value, null)
},
getValueFrom: function (value, item) {
if (value._param != null){
return this.params[value._param]
} else if (value._sub){
var options = copy(this.options)
options.force = null
options.currentItem = item
var result = this.handleQuery(value._sub, options, this.params)
this.addReferences(result.references)
return result.value
} else {
return value
}
},
deepQuery: function(source, tokens, options, callback){
var keys = Object.keys(source)
for (var key in source){
if (key in source){
var options = copy(this.options)
options.currentItem = source[key]
var result = this.handleQuery(tokens, options, this.params)
if (result.value){
return result
}
}
}
return null
}
}
function firstNonNull(args){
for (var i=0;i<arguments.length;i++){
if (arguments[i] != null){
return arguments[i]
}
}
}
function copy(obj){
var result = {}
if (obj){
for (var key in obj){
if (key in obj){
result[key] = obj[key]
}
}
}
return result
}