import { v4 as uuidv4 } from 'uuid' // import { Buffer } from 'buffer' // weird hack for remix client-side logic try { window.process = { env: {} } window.Buffer = () => {} // window.Buffer = Buffer } catch (err) { // nothing } // const babel = require('@babel/standalone') // babel.template = require('@babel/template').default // babel.t = require('@babel/types') // babel.generator = require('@babel/generator').default // babel.babylon = require('@babel/parser') // babel.prettier = require('prettier') const objList = [] const stringList = [] // ES6? // import { uuidv4 as uuid } from 'uuid' // import babel from '@babel/standalone' // import template from '@babel/template' // babel.template = template // import t from '@babel/types' // babel.t = t // import generator from '@babel/generator' // babel.generator = generator // import babylon from '@babel/parser' // babel.babylon = babylon // import prettier from 'prettier' // babel.prettier = prettier export const babel = () => {} export const local = {} export const t = () => {} // export const t = babel?.t export const getBabel = () => { return babel } export const uuid = uuidv4 export const pause = (value, opts) => { return stringify(value, opts) } export const save = (value, opts) => { return stringify(value, opts) } export const toJavascript = (value, opts = {}) => { let defaultOpts = { wrapInFunction: true, declarations: [], identifier: 'obj', keys: { obj: 1 }, dependancies: {}, mappings: [], seen: [], db: [] } Object.assign(defaultOpts, opts) opts = defaultOpts toJavascriptAux(value, opts) // sort declarations by dependancies for (let declaration of opts.declarations) { if (declaration.type === 'ExpressionStatement') { // nothing } else { let key = declaration.declarations[0].id.name let dependancies = opts.dependancies[key] let dependancyCheck = {} for (let dependancy of dependancies) { dependancyCheck[dependancy] = false } let sortableDeclarationIndex = 0 let insertionIndex = 0 for (let sortableDeclaration of opts.declarations) { if (sortableDeclaration.type === 'ExpressionStatement') { // nothing } else { let sortableDeclarationKey = sortableDeclaration.declarations[0].id.name if (dependancyCheck[sortableDeclarationKey] === false) { insertionIndex = sortableDeclarationIndex + 1 dependancyCheck[sortableDeclarationKey] = true } } // increment iterator index sortableDeclarationIndex++ } let declarationIndex = opts.declarations.indexOf(declaration) opts.declarations.splice(declarationIndex, 1) opts.declarations.splice(insertionIndex, 0, declaration) } } let program if (opts.wrapInFunction && !opts.moduleExport) { opts.declarations.push(t.returnStatement(t.identifier('obj'))) let expression = t.expressionStatement( t.callExpression( t.functionExpression(null, [], t.blockStatement(opts.declarations)), [] ) ) program = t.program([expression]) } else { if (opts.moduleExport) { opts.declarations.push( t.expressionStatement( t.assignmentExpression( '=', t.memberExpression(t.identifier('module'), t.identifier('exports')), t.identifier('obj') ) ) ) } program = t.program(opts.declarations) } let stringifiedProgram = getBabel().generator(program).code return getBabel().prettier.format(stringifiedProgram, { semi: false, parser: 'babel', useTabs: true }) } export const toJavascriptAux = (value, opts) => { if (typeof value === 'object') { if (!opts.seen.includes(value)) { // update map and seen opts.seen.push(value) let properties = createObjectProperties(value, opts) let declaration = t.variableDeclaration('let', [ t.variableDeclarator( t.identifier(opts.identifier), t.objectExpression(properties) ) ]) opts.declarations.unshift(declaration) for (let key of Object.keys(value)) { let property = properties.find(v => { return v.value.name === key }) if (property) { let identifier = property.value.name toJavascriptAux(value[key], { ...opts, identifier }) } } } } else if (typeof value === 'string') { let declaration = t.variableDeclaration('let', [ t.variableDeclarator( t.identifier(opts.identifier), t.stringLiteral(value) ) ]) opts.declarations.unshift(declaration) } } export const createObjectProperties = (value, opts) => { let properties = [] let dependancies = opts.dependancies[opts.identifier] if (!dependancies) dependancies = opts.dependancies[opts.identifier] = [] for (let key of Object.keys(value)) { if (value[key] === value) { opts.declarations.push( t.expressionStatement( t.assignmentExpression( '=', t.memberExpression( t.identifier(opts.identifier), t.identifier(key) ), t.identifier(opts.identifier) ) ) ) } else { if (!opts.db.includes(value[key])) { opts.db.push(value[key]) if (opts.keys[key] === undefined) { opts.keys[key] = 1 } let keyIncrement = opts.keys[key] if (keyIncrement == 1) { opts.mappings.push(key) } else { opts.mappings.push(key + keyIncrement) } opts.keys[key]++ } let identifier = opts.mappings[opts.db.indexOf(value[key])] dependancies.push(identifier) properties.push( t.objectProperty( t.identifier(key), t.identifier(identifier), false, key == identifier ) ) } } return properties } export const serialize = (value, opts = {}) => { opts.strictFunctions = false opts.serializeArrayProps = true return stringify(value, opts) } export const stringify = (value, opts = {}) => { let schema = { stringifier: stringifier, replace (key, value) { if (opts.firstRun) { opts.firstRun = !opts.firstRun return value } var after = opts.stringifier(key, value, opts) const type = typeof after.value if (type === 'object') { if (after === null || after.value === null) { const ret = after.value return ret } } else if (type === 'string') { const ret = opts.known.get(after.key) || setKnown(opts.known, opts.input, after) return ret } return after.value }, strictFunctions: true, firstRun: undefined, known: new Map(), input: [], output: [] } Object.keys(schema).forEach(key => { if (getsmart(opts, epp(key), { undefined: true }, true).undefined == true) { opts[key] = schema[key] } }) opts.virtual = opts.stringifier('', value, opts) for ( let i = parseInt(setKnown(opts.known, opts.input, opts.virtual)); i < opts.input.length; i++ ) { opts.firstRun = true try { opts.output[i] = JSON.stringify(opts.input[i], opts.replace, opts.space) } catch (err) { console.error(err) } } return '[' + opts.output.join(',') + ']' } export const replacer = (key, value) => { const opts = opts || {} if (opts.firstRun) { opts.firstRun = !opts.firstRun return value } var after = opts.stringifier(key, value, opts) // replace with if statements const type = typeof after.value if (type === 'object') { if (after === null) { const ret = after.value return ret } } else if (type === 'string') { const ret = opts.known.get(after.key) || setKnown(opts.known, opts.input, after) return ret } return after.value } export const setKnown = (known, input, virtual) => { var index = String(input.push(virtual.value) - 1) known.set(virtual.key, index) return index } export const stringifier = (key, val, opts) => { let ret = { value: val, key: val } if ( val instanceof Function && typeof val.toString === 'function' && (!opts.strictFunctions || typeof val.$scopes != 'undefined') ) { let known = opts.known.get(ret.key) ret = { value: known || { type: 'function', js: val.toString(), $scopes: val.$scopes, $context: val.$context, ...val }, key: val } if (ret.value.js == 'function () { [native code] }') return if (typeof known == 'undefined') setKnown(opts.known, opts.input, ret) } else if (ret.value === Infinity && typeof ret.value != 'string') { let known = opts.known.get(ret.key) ret = { value: known || { type: 'number', js: 'Infinity', $scopes: [], $context: {} }, key: val } if (typeof known == 'undefined') setKnown(opts.known, opts.input, ret) } else if (typeof ret.value === 'undefined') { ret = { value: { type: 'undefined', js: 'undefined' }, key: val } } else if (ret.value instanceof Array && opts.serializeArrayProps) { let known = opts.known.get(ret.key) ret = { value: known ? ret.value : { type: 'Array', js: ret.value, uuid: ret.value.uuid }, key: val } setKnown(opts.known, opts.input, ret) } return ret } export const primitives = value => { return value instanceof String ? String(value) : value } export const Primitives = (key, value) => { return typeof value === 'string' ? new String(value) : value } export const play = (text, opts) => { return parse(text, opts) } export const load = (text, opts = {}) => { opts.strictFunctions = false return parse(text, opts) } export const parse = (text, opts = {}) => { let schema = { // parser: eval('(function '+parser+')'), parser: parser(opts), value: {}, strictFunctions: true, noFunctions: false, firstPass: true, output: new Map() } Object.keys(schema).forEach(key => { if (getsmart(opts, epp(key), { undefined: true }, true).undefined == true) { opts[key] = schema[key] } }) // opts.parser = opts.parser.bind(opts) opts.input = JSON.parse(text, Primitives) opts.firstPass = false opts.input = opts.input.map(primitives) opts.value = opts.input[0] let isObject = typeof opts.value === 'object' && opts.value var tmp = isObject ? revive(opts.input, opts.output, opts.value, opts.parser, opts) : opts.value opts.replaceMode = true let ret = revive(opts.input, new Map(), tmp, opts.parser, opts) ret = opts.parser('', tmp, opts) return ret } export const parser = opts => { return function (key, val) { if (val.js && val.type === 'Array') { const ret = opts.input[opts.output.get(val)].js ret.uuid = opts.input[opts.output.get(val)].uuid return ret } else if ( val.js && val.type === 'function' && opts.replaceMode && !opts.noFunctions ) { let ret = opts.input[opts.output.get(val)] if (typeof ret == val.type) return ret let uuid = jsUUID() var fn var scopedEval if (val.$scopedEval && typeof val.$scopedEval == 'function') { scopedEval = val.$scopedEval } else { var fns = createScopedEval(uuid) try { fn = eval(`(${fns})`) var input = { val, smarts } scopedEval = fn(input) } catch (err) { console.log('Caught error evaling createScopedEval', err) } } ret = scopedEval({ val }) try { Object.defineProperty(ret, '$scopes', { value: val.$scopes, enumerable: true }) if (val.uuid) { ret.uuid = val.uuid } } catch (err) { if (opts.verbose) console.error(err) } try { Object.defineProperty(ret, '$context', { value: val.$context, enumerable: true }) } catch (err) { if (opts.verbose) console.error(err) } try { Object.defineProperty(ret, '$scopedEval', { value: scopedEval, enumerable: true }) } catch (err) { if (opts.verbose) console.error(err) } opts.input[opts.output.get(val)] = ret return ret } else if (opts.replaceMode) { return val } return Primitives(key, val) } } export const revive = (input, parsed, output, parser, opts) => { return Object.keys(output).reduce((output, key) => { var value = output[key] // if the value hasn't been revived yet if (value instanceof String) { var tmp = input[value] if (typeof tmp === 'object' && !parsed.get(tmp)) { parsed.set(tmp, value) output[key] = primitives( parser(key, revive(input, parsed, tmp, parser, opts)) ) } else { try { output[key] = primitives(parser(key, tmp)) } catch (err) { delete output[key] } } } else { try { if (opts.replaceMode) { // output[key] = primitives(parser(key, revive(input, parsed, value, parser, opts))) value = parser(key, value) if (typeof value === 'object' && !parsed.get(value)) { parsed.set(value, value) output[key] = primitives( parser(key, revive(input, parsed, value, parser, opts)) ) } else { try { output[key] = primitives(value) } catch (err) { delete output[key] } } } else { output[key] = primitives(parser(key, value)) } } catch (err) { delete output[key] } } return output }, output) } export const createScopedEval = uuid => { let ret = /*javascript*/ ` function createScopedEval(${uuid}){ // scopeCode ${uuid}.scopeCode = ${uuid}.scopeCode || ${uuid}.getBabel().template.ast('try{}catch(err){console.log(err)}') ${uuid}.previousScopeCode = ${uuid}.currentScopeCode || ${uuid}.scopeCode ${uuid}.currentScopeCode = ${uuid}.scopeCode.block.body.length ? ${uuid}.getBabel().template.ast('try{}catch(err){console.log(err)}') : ${uuid}.scopeCode if(${uuid}.previousScopeCode != ${uuid}.currentScopeCode){ ${uuid}.previousScopeCode.block.body.push( ${uuid}.currentScopeCode ) } ${uuid}.closureIndex = ${uuid}.closureIndex || 0 ${uuid}.closure = ${uuid}.getsmart.bind(this)(${uuid}, ${ /*javascript*/ `\`val.$scopes.\${${uuid}.closureIndex}\`` }, {}) ${uuid}.variableKeys = Object.keys(${uuid}.closure) ${uuid}.variableMap = ${uuid}.getsmart.bind(this)(${uuid}, ${ /*javascript*/ `\`val.$context.$variableMaps.\${${uuid}.closureIndex}\`` }, []) ${uuid}.allowedIdentifiers = ['let','var','const'] ${uuid}.variableKeys.forEach((key)=>{ if( typeof ${uuid}.variableMap[key] == 'string' && ${uuid}.allowedIdentifiers.indexOf(${uuid}.variableMap[key]) >= 0 ){ try{ ${uuid}.currentScopeCode.block.body.push( ${uuid}.getBabel().template.ast( ${ /*javascript*/ `\` \${${uuid}.variableMap[key]} \${key} = ${uuid}.val.$scopes[\${${uuid}.closureIndex}]['\${key}'] \`` } ) ) }catch(err){console.log(1,err)} try{ ${uuid}.currentScopeCode.block.body.push( ${uuid}.getBabel().template.ast( ${ /*javascript*/ `\` Object.defineProperty( ${uuid}.val.$scopes[\${${uuid}.closureIndex}], \${${uuid}.stringify(key)}, { get(){ return \${key} }, set(val){ \${key} = val }, enumerable: true } ) \`` } ) ) }catch(err){console.log(2,err)} } // console.log(${uuid}.scopeCode) }) // console.log(${uuid}.scopeCode) ${uuid}.closureIndex++ if(${uuid}.closureIndex >= ${uuid}.getsmart.bind(this)(${uuid}, 'val.$scopes.length', -1)){ // console.log(${uuid}.scopeCode) try{ ${uuid}.currentScopeCode.block.body.push( ${uuid}.getBabel().template.ast( ${ /*javascript*/ `\` return \${${uuid}.scopedEval('${uuid}')} \`` } ) ) }catch(err){console.log(3,err)} try{ ${uuid}.wrapper = ${uuid}.getBabel().template.ast( ${ /*javascript*/ `\` function anonymous(){} \`` } ) }catch(err){console.log(4,err)} // console.log(${uuid}.wrapper) // console.log(${uuid}.scopeCode) ${uuid}.wrapper.body.body.push(${uuid}.scopeCode) ${uuid}.scopeCode = ${uuid}.wrapper ${uuid}.scopeCode = ${uuid}.getBabel().generator( ${uuid}.scopeCode ).code // console.log(${uuid}.scopeCode) ${uuid}.scopeCode = eval("("+${uuid}.scopeCode+")") // console.log(${uuid}.scopeCode.toString()) try { ${uuid}.val.$scopedEval = ${uuid}.scopeCode() }catch(err){console.log(5,err)} // console.log(${uuid}.val.$scopedEval) // return ${uuid}.scopeCode.toString() return ${uuid}.val.$scopedEval } else { return eval(${ /*javascript*/ `\`(\${${uuid}.createScopedEval('${uuid}')})\`` })(${uuid}) } } ` return ret } export const defineVariable = uuid => { return /*javascript*/ ` ${uuid.variableType} ${uuid.variableKey} = ${uuid}.$scope[${uuid}.variableKey] Object.defineProperty( ${uuid}.$scope, ${uuid}.variableKey, { get(){ return ${uuid.variableKey} }, set(val){ ${uuid.variableKey} = val }, enumerable: true } ) ` } export const scopedEval = uuid => { let ret = /*javascript*/ `function scopedEval(${uuid}){ if(typeof ${uuid} == 'string'){ ${uuid} = { val: { js: ${uuid} } } } else if(typeof ${uuid} == 'function' && typeof ${uuid}.toString == 'function'){ ${uuid} = { val: { js: ${uuid}.toString() } } } try { ${uuid}.ret = eval('('+${uuid}.val.js+')') } catch(err1){ try { ${uuid}.ret = eval('({'+${uuid}.val.js+'})') ${uuid}.keys = Object.keys(${uuid}.ret) ${uuid}.ret = ${uuid}.ret[${uuid}.keys[0]] } catch(err2){ try { ${uuid}.ret = eval('({b:'+ ${uuid}.val.js +'})').b } catch(err3){ console.error(err1) console.error(err2) console.error(err3) } } } return ${uuid}.ret } ` return ret } export const jsUUID = (prefix = 'uuid') => { return prefix + uuid().replace(/-/g, '') } export const context = opts => { let uuid = gosmart.bind(this)(opts, 'path.context.scope.uuid', jsUUID()) return eval(/*javascript*/ ` ( function(){ ${contextObject(uuid)} return ${uuid} } )() `) } export const contextObject = uuid => { return /*javascript*/ ` let ${uuid} = { $$uuid: '${uuid}', $closure: {}, $variableMap: {}, $functionScoper: (func)=>{ Object.defineProperty( func, '$scopes', { value: (function(arr){ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2 })((typeof ${uuid} != 'undefined') ? ${uuid}.$scopes : []), enumerable: true } ) Object.defineProperty( func, '$context', { value: ${uuid} } ) return func }, $add: (type, name, value)=>{ ${uuid}.$closure[name] = value ${uuid}.$variableMap[name] = type }, $scopes: (function(arr){ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2 })((typeof $context == 'object') ? $context.$scopes : []), $variableMaps: (function(arr){ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2 })((typeof $context == 'object') ? $context.$variableMaps : []), $contexts: {}, $contextsList: [], $parentContexts: [], $contextStatus: "var", $mode: (eval("var ${uuid}1 = null"), (typeof ${ uuid + '1' } === "undefined")) ? "strict" : "non-strict", } ${uuid}.$functionScoper = ${uuid}.$functionScoper(${uuid}.$functionScoper) ${uuid}.$scopes.splice(0,0,${uuid}.$closure) ${uuid}.$variableMaps.splice(0,0,${uuid}.$variableMap) var globalThis = globalThis || global || window || {} ${uuid}.$contextStatus = ${uuid}.$mode == 'strict' ? '' : 'var' try { eval(${ /*javascript*/ `\`\${${uuid}.$contextStatus} $context = $context || ${uuid}\`` }) } catch(err){ ${uuid}.$contextStatus = '' } eval(${ /*javascript*/ `\`\${${uuid}.$contextStatus} $context = $context || ${uuid}\`` }) if(typeof $context == 'object' && $context != ${uuid} && $context.$contexts instanceof Object){ $context.$contexts[${uuid}.$$uuid] = $context.$contexts[${uuid}.$$uuid] || [] ${uuid}.$$instance = $context.$contexts[${uuid}.$$uuid].push(${uuid})-1 ${uuid}.$parentContexts.push($context) $context.$contextsList.push(${uuid}) } if(!globalThis.$contexts){ globalThis.$contexts = {} globalThis.$contexts[${uuid}.$$uuid] = [${uuid}] globalThis.$contextsList = [${uuid}] ${uuid}.$$instance = 0 } else if( globalThis.${uuid}s instanceof Object && ${uuid}.$parentContexts.length == 0 && typeof ${uuid}.$$instance == 'undefined' ){ globalThis.${uuid}s[${uuid}.$$uuid] = globalThis.$contexts[${uuid}.$$uuid] || [] ${uuid}.$$instance = globalThis.$contexts[${uuid}.$$uuid].push(${uuid})-1 globalThis.$contextsList.push(${uuid}) } { let $context = ${uuid} } ` } export const createContext = opts => { schema(opts, { wrapBody: true }) let node = opts.aster(/*javascript*/ ` ${contextObject(opts.uuid)} `) node[0].declarations[0].contextDeclaration = true // so the $functionScoper function doesn't get wrapped or have $context inserted let property3 = node[0].declarations[0].init.properties[3] property3.value.scoperWrapped = true property3.value.body.scopeInitialized = true let property3ScopesValue = property3.value.body.body[0].expression.arguments[2].properties[0].value property3ScopesValue.callee.scoperWrapped = true property3ScopesValue.callee.body.scopeInitialized = true let property3ForStatement = property3ScopesValue.callee.body.body[0] property3ForStatement.body.scopeInitialized = true property3ForStatement.init.declarations[0].inScope = true property3ForStatement.init.declarations[1].inScope = true // so the $add function doesn't get wrapped or have $context inserted let property4 = node[0].declarations[0].init.properties[4] property4.value.scoperWrapped = true property4.value.body.scopeInitialized = true // so the $scopes self-invoking function doesn't get wrapped or have $context inserted let property5 = node[0].declarations[0].init.properties[5] property5.value.callee.scoperWrapped = true property5.value.callee.body.scopeInitialized = true let property5ForStatement = property5.value.callee.body.body[0] property5ForStatement.body.scopeInitialized = true property5ForStatement.init.declarations[0].inScope = true property5ForStatement.init.declarations[1].inScope = true // so the $variableMaps self-invoking function doesn't get wrapped or have $context inserted let property6 = node[0].declarations[0].init.properties[6] property6.value.callee.scoperWrapped = true property6.value.callee.body.scopeInitialized = true let property6ForStatement = property6.value.callee.body.body[0] property6ForStatement.body.scopeInitialized = true property6ForStatement.init.declarations[0].inScope = true property6ForStatement.init.declarations[1].inScope = true const node6 = node[6] // make sure try statement block doesn't get scoped either node6.block.scopeInitialized = true // make sure catch statement block doesn't get scoped either node6.handler.body.scopeInitialized = true const node8 = node[8] // make sure if statement block doesn't get scoped either node8.consequent.scopeInitialized = true const node9 = node[9] // make sure if statement block doesn't get scoped either node9.consequent.scopeInitialized = true // make sure else if statement block doesn't get scoped either node9.alternate.consequent.scopeInitialized = true const node10 = node[10] node10.scopeInitialized = true node10.inheritScope = true node[node.length - 1].lastContextNode = true if (opts.wrapBody) { let bodyWrapper = node[node.length - 1] bodyWrapper.body.push(...opts.path.node.body) } addBindingsToContext({ ...opts, node }) // let addContextToScopeNode = scopeVar({ // uuid, // key: '$context', // type: 'let', // aster // }) // wrapper.body.splice(1,0,addContextToScopeNode) return node } export const createInlineContext = opts => { let wrapperString = /*javascript*/ ` for(let ${opts.uuid} = function(){ // node goes here return ${opts.uuid} }() ; a<1;a++){} ` let inlineContextNode = opts.aster(wrapperString).init.declarations[0] let contextBody = createContext({ ...opts, wrapBody: false }) inlineContextNode.init.callee.body.body.splice(0, 0, ...contextBody) inlineContextNode.contextDeclaration = true return inlineContextNode } export const addBindingsToContext = opts => { for (let key in opts.path.scope.bindings) { let binding = opts.path.scope.bindings[key] if (binding.kind == 'var') { let newNode = scopeVar({ ...opts, key, type: binding.kind }) opts.node.splice(opts.node.length - 1, 0, newNode) } } } export const scopeVarCode = opts => { let ret = /*javascript*/ ` Object.defineProperty( ${opts.uuid}.$closure, ${stringify(opts.key)}, { get(){ return ${opts.key} }, set(val){ ${opts.key} = val }, enumerable: true } ) && (${opts.uuid}.$variableMap["${opts.key}"] = "${opts.type}") ` return ret } export const scopeVarInlineCode = opts => { let ret = /*javascript*/ ` let ${jsUUID()} = ( ${scopeVarCode(opts)} ) ` return ret } export const scopeVar = (opts = {}) => { let string let thirdArg let node if (opts.inline) { string = scopeVarInlineCode(opts) node = opts.aster(string) thirdArg = node.declarations[0].init.left.arguments[2] node.declarations[0].inScope = true } else { string = scopeVarCode(opts) node = opts.aster(string) thirdArg = node.expression.left.arguments[2] } let getter = thirdArg.properties[0] let setter = thirdArg.properties[1] getter.body.scopeInitialized = true setter.body.scopeInitialized = true getter.body.scoperWrapped = true setter.body.scoperWrapped = true getter.scoperWrapped = true setter.scoperWrapped = true if (opts.inline) return node.declarations[0] return node } export const functionWrapper = (uuid, path, aster) => { let wrapper = aster(/*javascript*/ ` ${uuid}.$functionScoper() `) wrapper.expression.arguments.push(path.node) return wrapper } export const bodyInsert = (index, body, aster, ...things) => { body.splice(index, 0, ...things) return things.length } export const initBlock = (path, aster) => { if (!path.node.scopeInitialized) { path.node.scopeInitialized = true let uuid = getPathUUID({ path }) let contextNode = createContext({ uuid, aster, path }) path.node.body = contextNode } } export const getNodeUUID = opts => { if ( opts.node && opts.node.type != 'BlockStatement' && opts.node.type != 'Program' ) return getNodeUUID({ ...opts, node: opts.node.body || opts.node.block }) return gosmart.bind(this)(opts.node, 'uuid', jsUUID()) } export const getPathUUID = opts => { if ( opts.path.context.scope.path.node.inheritScope || opts.path.scope.path.node.inheritScope ) return getPathUUID({ ...opts, path: opts.path.parentPath }) return getNodeUUID({ ...opts, node: opts.path.context.scope.path.node }) } export const babelPlugin = babel => { const aster = babel.template.ast let metaVisitor = { Program (path) { initBlock(path, aster) }, BlockStatement (path) { initBlock(path, aster) }, ForInStatement () { // nothing }, // ForInStatement (path) { // path = path // }, ObjectMethod (path) { let name = path.node.key.name let replacement = aster(/*javascript*/ ` let a = { ${name}: function ${name}(){} } `) replacement = replacement.declarations[0].init.properties[0] replacement.value.body = path.node.body replacement.value.params = path.node.params path.replaceWith(replacement) }, Function (path) { if ( path.type != 'FunctionDeclaration' && !path.node.scoperWrapped && !path.node.body.scoperWrapped ) { path.node.scoperWrapped = true path.node.body.scoperWrapped = true let uuid = getPathUUID({ path }) let replacement = functionWrapper(uuid, path, aster) path.replaceWith(replacement) } }, FunctionDeclaration (path) { if (!path.node.scoped) { path.node.scoped = true const parentBlock = path.scope.parent try { parentBlock.block.body.forEach(node => { if (node.lastContextNode) { let uuid = getPathUUID({ path }) let newNode = aster(/*javascript*/ ` ${uuid}.$functionScoper(${path.node.id.name}) `) node.body.splice(1, 0, newNode) throw new Error('break foreach') } }) } catch (err) { // nothing } } }, VariableDeclarator (path) { if (!path.node.inScope) { path.node.inScope = true let parentPath = getsmart.bind(this)(path, 'parentPath', undefined) if ( // this is for inline let and const declarations in normal // js blocks (parentPath.node.kind == 'let' || parentPath.node.kind == 'const') && // we check the length of declarations because we only have to do inline replacement // if there's a chance another declaration might use a former one parentPath.node.declarations.length > 1 && !( parentPath.parentPath.node.type == 'ForInStatement' || parentPath.parentPath.node.type == 'ForOfStatement' || parentPath.parentPath.node.type == 'ForStatement' ) ) { let uuid = getPathUUID({ path }) if (uuid) { let indexInParent = parentPath.node.declarations.indexOf(path.node) let newDeclaration = scopeVar({ aster, inline: true, uuid, key: parentPath.node.declarations[indexInParent].id.name, type: parentPath.node.kind }) parentPath.node.declarations.splice( indexInParent + 1, 0, newDeclaration ) } } else if ( // (parentPath.node.kind == 'let' || parentPath.node.kind == 'var' || parentPath.node.kind == 'const') && // only do this for singular declarations parentPath.node.declarations.length < 2 && // and check if variable is declared inside a ForX statement (parentPath.parentPath.node.type == 'ForInStatement' || parentPath.parentPath.node.type == 'ForOfStatement' || parentPath.parentPath.node.type == 'ForStatement') ) { let uuid = getPathUUID({ path }) if (uuid) { let indexInParent = parentPath.node.declarations.indexOf(path.node) let newNode = scopeVar({ aster, uuid, key: parentPath.node.declarations[indexInParent].id.name, type: parentPath.node.kind }) parentPath.parentPath.node.body.body.splice(0, 0, newNode) } } else if ( // this is a special case for when ForStatements get their own scope (parentPath.node.kind == 'let' || parentPath.node.kind == 'const') && // we check the length of declarations because we only have to do inline replacement // if there's a chance another declaration might use a former one parentPath.node.declarations.length > 1 && parentPath.parentPath.node.type == 'ForStatement' ) { // if the first declaration isn't our context declaration, insert one let uuid = gosmart.bind(this)(path, 'scope.uuid', jsUUID()) if (!parentPath.node.declarations[0].contextDeclaration) { let inlineContextDeclaration = createInlineContext({ path, uuid, aster }) parentPath.node.declarations.splice(0, 0, inlineContextDeclaration) } if (uuid) { let indexInParent = parentPath.node.declarations.indexOf(path.node) let newDeclaration = scopeVar({ aster, inline: true, uuid, key: parentPath.node.declarations[indexInParent].id.name, type: parentPath.node.kind }) parentPath.node.declarations.splice( indexInParent + 1, 0, newDeclaration ) } } else if ( parentPath.node.kind == 'let' || parentPath.node.kind == 'const' ) { let uuid = getPathUUID({ path }) if (uuid) { let indexInParent = parentPath.node.declarations.indexOf(path.node) let newNode = scopeVar({ aster, uuid, key: parentPath.node.declarations[indexInParent].id.name, type: parentPath.node.kind }) parentPath.insertAfter(newNode) } } else { // let uuid = getPathUUID({path}) // if(uuid){ // let indexInParent = parentPath.node.declarations.indexOf(path.node) // let newNode = scopeVar({ // aster, // uuid, // key: parentPath.node.declarations[indexInParent].id.name, // type: parentPath.node.kind // }) // parentPath.insertAfter(newNode) // } } } } } let ret = { visitor: metaVisitor // visitor: { // Program(path){ // path.traverse(metaVisitor) // } // } } return ret } export const transform = (src, opts = {}) => { return getBabel().transform(src, { plugins: [babelPlugin], ...opts }) } /** non-parser stuff */ export const stripUuids = (thing, seen = []) => { try { delete thing.uuid } catch { // nothing } if (typeof thing === 'object') { const keys = Object.keys(thing) for (const key of keys) { const val = thing[key] if (!seen.includes(val)) { seen.push(val) stripUuids(val, seen) } } } } export const dupe = (obj, opts = {}) => { return parse(stringify(obj, opts), opts) } export const clone = (obj, opts = {}) => { return dupe(obj, opts) } export const schema = (obj1, obj2, opts = {}) => { if (!opts.noSchemaClone) { obj2 = clone(obj2, opts) } return merge(obj1, obj2, { ...opts }) } export const create = (obj1, obj2, opts) => { let ret = merge(obj1, obj2, { clone: true, ...opts }) return ret } export const merge = (value1, value2, opts = {}, seen = new Map()) => { if (seen.has(value1)) return seen.get(value1) if (value1 instanceof Array && value2 instanceof Array) { return value1 } // base case non-basic value if (basic(value1) && !opts.overwrite) { return value1 } if (opts.overwrite && basic(value2)) { return value2 } if (!basic(value1) && basic(value2)) { return value1 } if (opts.clone) { value1 = clone(value1) value2 = clone(value2) } let props = Object.keys(value2) props.forEach(prop => { let propertyValue1 = value1[prop] if (prop in value1 && basic(propertyValue1) && !opts.overwrite) { return } let propertyValue2 = value2[prop] seen.set(value1, value1) let newVal = propertyValue2 if (prop in value1) { newVal = merge.bind(this)( propertyValue1, propertyValue2, { ...opts, ...{ clone: false } }, seen ) } if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && value1 ) { const setToUse = this.$set || local.vue.Vue.set setToUse?.(value1, prop, newVal) if ( typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { value1[prop] = newVal if ( getsmart.bind(this)(local.vue, 'store', false) && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } }) return value1 } export const basic = value => { let valueType = typeof value let ret = !( valueType == 'object' || valueType == 'array' || valueType == 'function' ) || value === null return ret } export const mod = (args, mod) => { return mod(args) || args } // transform(value, fn, path, ret={}){ // return forEach(value, fn, path, ret) // }, export const deepForEach = ( value, fn, path, ret = {}, seens = { originals: [], clones: [] }, first = true ) => { path = path || '' if (first) { value = { '': value } } // if(!(typeof value == 'string' || typeof value == 'boolean' || typeof value == 'number')){ // seens.originals.push(value) // } if (Array.isArray(value)) { forEachArray(value, fn, path, ret, seens) } else if (typeof value == 'object') { forEachObject(value, fn, path, ret, seens) } return ret[''] } export const forEachObject = (obj, fn, path, ret, seens) => { for (const key in obj) { const deepPath = path ? `${path}.${key}` : key let primitive = typeof obj[key] == 'string' || typeof obj[key] == 'boolean' || typeof obj[key] == 'number' if (primitive || seens.originals.indexOf(obj[key]) < 0) { if (!primitive) { seens.originals.push(obj[key]) } // Note that we always use obj[key] because it might be mutated by forEach fn(obj[key], key, obj, deepPath, ret, seens) deepForEach(obj[key], fn, deepPath, ret, seens, false) } } } export const forEachArray = (array, fn, path, ret = {}, seens) => { array.forEach((value, index, arr) => { let primitive = typeof value == 'string' || typeof value == 'boolean' || typeof value == 'number' if (primitive || seens.originals.indexOf(value) < 0) { if (!primitive) { seens.originals.push(value) } const deepPath = `${path}.${index}` fn(value, index, arr, deepPath, ret, seens) // Note that we use arr[index] because it might be mutated by forEach deepForEach(arr[index], fn, deepPath, ret, seens, false) } }) } export const setThing = ({ option, list = getsmart.bind(this)(objList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype, push, strings, targets } = {}) => { let index = thingIndex({ option, list, obj, keys, keymatchtype, strings }) if (obj == 'debug') { console.log('index') console.log(index) console.log('list') console.log(list) } if (index >= 0 && list) { if (targets && targets.length && typeof targets.length == 'number') { for (var i = 0; i < targets.length; i++) { let value = getsmart.bind(this)(option, targets[i], undefined) if (value) { setsmart.bind(this)(list[index], targets[i], value) } } } else { list.splice(index, 1, option) if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) ) { if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else if ( getsmart.bind(this)(local.vue, 'store', false) && !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } // list[index] = option } else if (push && list) { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { list.splice(list.length, 0, option) if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { list.push(option) } index = list.length - 1 } return index } export const setThings = ({ options, list = getsmart.bind(this)(objList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype, push, async } = {}) => { if (options && options instanceof Array && list) { for (let option of options) { if (async) { new Promise(() => { setThing({ option, list, obj, keys, keymatchtype, push }) }) } else { setThing({ option, list, obj, keys, keymatchtype, push }) } } } return list } export const optIn = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype, index ) => { if (typeof option === 'object') { obj = true } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { return index ? list.indexOf(option) : true } else if (obj && list && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (!(keys && typeof keys.length == 'number')) return for (var indKey = 0; indKey < keys.length; indKey++) { if (keymatchtype == 'broad') { if ( list[i] && getsmart.bind(this)(list[i], keys[indKey], undefined) == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined ) { return index ? i : true } else if ( list[i] && typeof list[i] == 'string' && list[i] == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(option, keys[indKey], undefined) !== undefined ) { return index ? i : true } } else { if ( list[i] && getsmart.bind(this)(list[i], keys[indKey], undefined) == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined ) { if (indKey == keys.length - 1) { return index ? i : true } } else if ( list[i] && typeof list[i] == 'string' && list[i] == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(option, keys[indKey], undefined) !== undefined ) { if (indKey == keys.length - 1) { return index ? i : true } } } } } } return index ? -1 : false } export const thingIn = ({ option, list = getsmart.bind(this)(objList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype, retIndex // vue = local.vue } = {}) => { if (typeof option === 'object') { obj = true } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { if (retIndex) { return list.indexOf(option) } else { return true } } else if (obj && list && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (!(keys && typeof keys.length == 'number')) return for (var indKey = 0; indKey < keys.length; indKey++) { if (keymatchtype == 'broad') { if ( list[i] && getsmart.bind(this)(list[i], keys[indKey], undefined) == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined ) { if (retIndex) { return i } else { return true } } else if ( list[i] && typeof list[i] == 'string' && list[i] == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(option, keys[indKey], undefined) !== undefined ) { if (retIndex) { return i } else { return true } } } else { if ( list[i] && getsmart.bind(this)(list[i], keys[indKey], undefined) == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(list[i], keys[indKey], undefined) !== undefined ) { if (indKey == keys.length - 1) { if (retIndex) { return i } else { return true } } } else if ( list[i] && typeof list[i] == 'string' && list[i] == getsmart.bind(this)(option, keys[indKey], undefined) && getsmart.bind(this)(option, keys[indKey], undefined) !== undefined ) { if (indKey == keys.length - 1) { if (retIndex) { return i } else { return true } } } } } } } if (retIndex) { return -1 } else { return false } } export const optsIn = ( options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (!(options instanceof Array)) return true for (let option of options) { // if(typeof option === 'object'){ // obj = true // } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { // return true } else if (obj && list) { for (var i = 0; i < list.length; i++) { if (!optIn(option, list[i], obj, keys, keymatchtype)) { return false } } } else { return false } } return true } export const thingsIn = ({ options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (!(options instanceof Array)) return true for (let option of options) { // if(typeof option === 'object'){ // obj = true // } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { // return true } else if (obj && list && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (!optIn(option, list[i], obj, keys, keymatchtype)) { return false } } } else { return false } } return true } export const anyOptsIn = ( options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (!(options instanceof Array)) return false for (let option of options) { // if(typeof option === 'object'){ // obj = true // } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { return true } else if (obj && list && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (optIn(option, list[i], obj, keys, keymatchtype)) { return true } } } } return false } export const anyThingsIn = ({ options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (!(options instanceof Array)) return false for (let option of options) { // if(typeof option === 'object'){ // obj = true // } if (!obj && list && list.indexOf && list.indexOf(option) >= 0) { return true } else if (obj && list && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (optIn(option, list[i], obj, keys, keymatchtype)) { return true } } } } return false } export const optIndex = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (typeof option === 'object') { obj = true } if (obj && list && keys && typeof list.length == 'number') { for (var i = 0; i < list.length; i++) { if (optIn(option, list, obj, keys, keymatchtype)) { return i } } } else if (list) { return list.indexOf(option) } return -1 } export const thingIndex = ({ option, list, obj, keys = ['uuid', '_id', 'id'], keymatchtype, strings } = {}) => { if (typeof option === 'object') { obj = true } if (obj && list && keys) { let index = thingIn({ option, list, obj, keys, keymatchtype, strings, retIndex: true }) return index } else if (list) { return list.indexOf(option) } return -1 } export const pushOpt = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype, index ) => { if ( typeof list == 'object' && !optIn(option, list, obj, keys, keymatchtype) ) { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { list.splice(list.length, 0, option) if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { list.push(option) } } return index ? optIn(option, list, obj, keys, keymatchtype, index) : optIn(option, list, obj, keys, keymatchtype, index) } export const addOpt = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype, index ) => { if (typeof list == 'object') { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { list.splice(list.length, 0, option) if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { list.push(option) } } return index ? optIn(option, list, obj, keys, keymatchtype, index) : optIn(option, list, obj, keys, keymatchtype, index) } export const pushThing = ({ option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if ( typeof list == 'object' && !thingIn({ option, list, obj, keys, keymatchtype }) ) { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { list.splice(list.length, 0, option) if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { list.push(option) } } } export const pushOpts = ( options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (!(options instanceof Array)) return for (let option of options) { pushOpt(option, list, obj, keys, keymatchtype) } } export const pushThings = ({ options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (!(options instanceof Array)) return for (let option of options) { pushThing({ option, list, obj, keys, keymatchtype }) } } export const popOpt = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (typeof list == 'object' && optIn(option, list, obj, keys, keymatchtype)) { list.splice(optIndex(option, list, obj, keys, keymatchtype), 1) if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } } } export const popThing = ({ option, list = getsmart.bind(this)(stringList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if ( typeof list == 'object' && thingIn({ option, list, obj, keys, keymatchtype }) ) { list.splice( thingIndex({ option, list, obj, keys, keymatchtype }), 1 ) if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) || getsmart.bind(this)(local.vue, 'store', false) ) { if ( !localStorage.getItem('vuexWriteLock') && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } } } export const popOpts = ( options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (!(options instanceof Array)) return for (let option of options) { popOpt(option, list, obj, keys, keymatchtype) } } export const popThings = ({ options, list = getsmart.bind(this)(stringList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (!(options instanceof Array)) return for (let option of options) { popOpt(option, list, obj, keys, keymatchtype) } } export const toggleOpt = ( option, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (optIn(option, list, obj, keys, keymatchtype)) { popOpt(option, list, obj, keys, keymatchtype) } else { pushOpt(option, list, obj, keys, keymatchtype) } } export const toggleThing = ({ option, list = getsmart.bind(this)(stringList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (optIn(option, list, obj, keys, keymatchtype)) { popOpt(option, list, obj, keys, keymatchtype) } else { pushOpt(option, list, obj, keys, keymatchtype) } } export const toggleOpts = ( options, list = getsmart.bind(this)(stringList), obj, keys = ['uuid', '_id', 'id'], keymatchtype ) => { if (!(options instanceof Array)) return for (let option in options) { toggleOpt(option, list, obj, keys, keymatchtype) } } export const toggleThings = ({ options, list = getsmart.bind(this)(stringList), obj = true, keys = ['uuid', '_id', 'id'], keymatchtype } = {}) => { if (!(options instanceof Array)) return for (let option in options) { if (optIn(option, list, obj, keys, keymatchtype)) { popOpt(option, list, obj, keys, keymatchtype) } else { pushOpt(option, list, obj, keys, keymatchtype) } } } // no use right now // export const ratchetOpt = ( // option, // list, // obj, // keys = ['uuid', '_id', 'id'], // keymatchtype // ) => { // // find(obj, property, equals){ // // if(getsmart.bind(this)(obj, 'constructor', undefined) == Array){ // // for(var i=0; i { if (!property && obj && typeof obj == 'string') { property = obj.split('.') try { obj = eval(property[0]) } catch (err) { // console.error(err) obj = property[0] } property = property.slice(1, property.length) } if (!property) { if (context) { return { value: defaultValue, undefined: true } } else { return defaultValue } } // If the property list is in dot notation, convert to array if (typeof property == 'string') { property = parsePropertyPath(property) } else if (getsmart.bind(this)(property, 'constructor', false) !== Array) { if (context) { return { value: defaultValue, undefined: true, err: 'properties path @property argument was not passed properly' } } else { return defaultValue } } let deepGetByArray = deepGetByArrayUnbound.bind(this) return deepGetByArray(obj, property, defaultValue) // In order to avoid constantly checking the type of the property // we separate the real logic out into an inner function. function deepGetByArrayUnbound (obj, propsArray, defaultValue) { // This case getting to the last property but it not being ultimately defined // Not just having a value of undefined if ( propsArray.length > 0 && context && typeof obj == 'object' && obj !== null && !(ee(propsArray[0]) in obj) ) { return { value: defaultValue, undefined: true } } // If we have reached an undefined/null property // then stop executing and return the default value. // If no default was provided it will be undefined. if ( typeof obj == 'undefined' || obj == null || (schema && obj.constructor.name !== schema) ) { if (context) { let undef = true if (propsArray.length === 0) { undef = false } return { value: defaultValue, undefined: undef, schema: schema && obj.constructor.name === schema } } else { return defaultValue } } // If the path array has no more elements, we've reached // the intended property and return its value if (propsArray.length === 0) { if (context) { return { value: obj, undefined: false } } else { return obj } } // Prepare our found property and path array for recursion var nextObj = obj[ee(propsArray[0])] var remainingProps = propsArray.slice(1) return deepGetByArray(nextObj, remainingProps, defaultValue) } } export const escapePropertyPath = (path = '') => { let newPath = escapeEscapes(path) return '["' + newPath + '"]' } export const epp = (path = '') => { return escapePropertyPath(path) } export const escapeEscapes = (path = '') => { let newPath = '' for (let i in path) { i = +i let char = path[i] if (i > 0 && i < path.length - 1) { let prevChar = path[i - 1] let nextChar = path[i + 1] let openingArrayPath = char === '"' && prevChar === '[' // && (nextChar !== "\\" || i === path.length-1) let closingArrayPath = char === '"' && nextChar === ']' && prevChar !== '\\' // let offset = 0 // if(openingArrayPath) offset = 1 if (openingArrayPath || closingArrayPath) { newPath += '\\' // path = path.slice(0,i+offset)+"\\"+path.slice(i+offset,path.length) } } newPath += char } return newPath } export const ee = (path = '') => { return escapeEscapes(path) } // TODO // Make parsing use \" or \' // Currently only uses \" export const parsePropertyPath = (path = '') => { let array = [] let readingArrayBasedPath = false let i = 0 let push = false let pushed = false while (i < path.length) { let arrayPathStart = path[i] == '[' && path[i + 1] == '"' let escapedStart = !(path[i + 1] !== '\\' || i === 0) if (readingArrayBasedPath) { // we found the end of an array delimited path let arrayPathEnd = path[i] == '"' && path[i + 1] == ']' let escapedEnd = !(path[i - 1] !== '\\' || i == 0) if (arrayPathEnd && !escapedEnd) { i += 1 readingArrayBasedPath = false if (!pushed) push = true } else { // if the path includes an "escaped" array based path begin or end value // do not push the escape character if ( (path[i] == '\\' && path[i + 1] == '"' && path[i + 2] == ']') || (path[i - 1] == '[' && path[i] == '\\' && path[i + 1] == '"') ) { // nothing } else { array[array.length - 1] += path[i] } } } else if (path[i] == '.') { if (!pushed) push = true } // we found the start of an array delimited path else if (arrayPathStart && !escapedStart) { readingArrayBasedPath = true if (!pushed) push = true i += 1 } else { if (i == 0) array.push('') array[array.length - 1] += path[i] } i++ if (push && i < path.length) { pushed = true array.push('') push = false } else { pushed = false } } return array } export const ppp = (path = '') => { return this.parsePropertyPath(path) } export const parsePropertyArray = pathArray => { let path = '' if (pathArray instanceof Array) { pathArray.forEach(subPath => { path += epp(subPath) }) } else if (typeof pathArray === 'string') { return path } return path } export const ppa = pathArray => { return this.parsePropertyArray(pathArray) } export const pathToArray = path => { if (typeof path == 'string') { return parsePropertyPath(path) } else { return path } } export const pathToString = path => { if (typeof path == 'string') { let ret = parsePropertyPath(path) ret = parsePropertyArray(ret) return ret } else { let ret = parsePropertyArray(path) return ret } } export const setsmart = (obj, property, value, context?: any) => { if (!property && typeof obj == 'string') { property = obj.split('.') try { obj = eval(property[0]) } catch (err) { obj = property[0] } property = property.slice(1, property.length) } // If the property list is in dot notation, convert to array if (typeof property == 'string') { property = parsePropertyPath(property) } else if (getsmart.bind(this)(property, 'constructor', false) !== Array) { if (context) { return { value: value, undefined: true, err: 'properties path @property argument was not passed properly' } } else { return value } } // if no obj make obj if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) obj = {} let deepSetByArray = deepSetByArrayUnbound.bind(this) if (property) { return deepSetByArray(obj, property, value) } else { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && obj ) { const setToUse = this.$set || local.vue.Vue.set setToUse(obj, undefined, value) if ( typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { obj = value if ( getsmart.bind(this)(local.vue, 'store', false) && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } if (context) { return { value: obj, undefined: false, err: 'there were no properties passed' } } else { return obj } } // In order to avoid constantly checking the type of the property // we separate the real logic out into an inner function. function deepSetByArrayUnbound (obj, propsArray, value) { // If the path array has only 1 more element, we've reached // the intended property and set its value if (propsArray.length == 1) { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && obj ) { const setToUse = this.$set || local.vue.Vue.set setToUse(obj, ee(propsArray[0]), value) if ( typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { // TODO: make parent object new object so that react can see the change obj[ee(propsArray[0])] = value if ( getsmart.bind(this)(local.vue, 'store', false) && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } if (context) { return { value: obj[ee(propsArray[0])], undefined: false } } else { return obj[ee(propsArray[0])] } } // Prepare our path array for recursion var remainingProps = propsArray.slice(1) // check if next prop is object if (typeof obj[ee(propsArray[0])] !== 'object') { // If we have reached an undefined/null property if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && obj ) { const setToUse = this.$set || local.vue.Vue.set setToUse(obj, ee(propsArray[0]), {}) if ( typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { obj[ee(propsArray[0])] = {} if ( getsmart.bind(this)(local.vue, 'store', false) && typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } } return deepSetByArray(obj[ee(propsArray[0])], remainingProps, value) } } export const deletesmart = (obj, property) => { if (!property && typeof obj == 'string') { property = obj.split('.') try { obj = eval(property[0]) } catch (err) { // console.error(err) obj = property[0] } property = property.slice(1, property.length) } // If the property list is in dot notation, convert to array if (typeof property == 'string') { property = parsePropertyPath(property) } let parentPathArray = property.slice(0, property.length - 1) let path = property[property.length - 1] let parentObj = getsmart(obj, parentPathArray, {}) if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && obj ) { this.$delete(parentObj, path) } else { delete parentObj[path] } } export const pushSmart = (array, value) => { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && array ) { array.push(value) if ( typeof getsmart.bind(this)(window, '$store.commit', undefined) == 'function' ) { window.$store.commit(local.vue.basePath || 'graph/thing') } } else { array.push(value) } } export const gosmart = (obj, property, value, context, schema) => { // stands for get or set smart var get = getsmart.bind(this)( obj, property, value, true, schema ? absoluteType.bind(this)(value) : false ) if (get.undefined || (schema && get.schema === false)) { get = setsmart.bind(this)(obj, property, get.value, context) if (context) { return get } else { return get } } else { return get.value } } export const gosmarter = (obj, property, value, context, schema = true) => { return gosmart.bind(this)(obj, property, value, context, schema) } export const absoluteType = value => { let type try { type = value.constructor.name } catch (e) { if (typeof value === 'undefined') type = 'undefined' if (value === null) type = 'null' } return type } export const vgosmart = (obj, property, value, context) => { // stands for v-model get or set smart // return value from property path, either gotten or smartly set return { get: () => { var get = getsmart.bind(this)(obj, property, value, true) if (get.undefined) { get = setsmart.bind(this)(obj, property, get.value, context) } if (context) { return get } else { return getsmart.bind(this)(get, 'value', get) } }, set: val => { setsmart.bind(this)(obj, property, val) } } } export const getsmartval = (obj, property, defaultValue) => { // get the value of a property path based off its type let target = getsmart.bind(this)(obj, property, defaultValue) if (target && target.type) { if (target[target.type]) { return target[target.type] } else { return defaultValue } } else if (target) { return target } return defaultValue } export const safestring = something => { return smarts.stringify(something || '') } export const safeparse = something => { return smarts.parse(something || '') } export const flatten = (arrays, func = i => i) => { const flat = [] arrays.forEach(array => { if (Array.isArray(array)) { flat.push(...flatten(array)) } else { flat.push(func(array)) } }) return flat } export const mapsmart = (list, keyProperty = 'title', returnExistant) => { return new Promise((resolve, reject) => { if (!keyProperty) { reject() } else if (list && typeof list.length == 'number') { if (list.length == 0) { if ( (returnExistant && getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) || !returnExistant ) { resolve(true) } else if (returnExistant) { resolve(false) } else { resolve() } } if (!list.mapped || typeof list.mapped === 'boolean') { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && list ) { const setToUse = this.$set || local.vue.Vue.set setToUse(list, 'mapped', {}) } else { list['mapped'] = {} } } for (var i = 0; i < list.length; i++) { if (typeof list[i] !== 'string') { if ( getsmart.bind(this)(local.vue, 'reactiveSetter', false) && getsmart.bind(this)( this, '$set', getsmart.bind(this)(local.vue, 'Vue.set', false) ) && list.mapped ) { const setToUse = this.$set || local.vue.Vue.set setToUse(list.mapped, list[i][keyProperty], list[i]) } else { list['mapped'][list[i][keyProperty]] = list[i] } if (i == list.length - 1) { if ( (returnExistant && getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) || !returnExistant ) { resolve(true) } else if (returnExistant) { resolve(false) } else { resolve() } } } // else if(populate){ // var funCounter = this.funCounter // this.funCounter = this.funCounter + 1 // getThing({ // thing: list[i], // clientId: this._uid, // funCounter // }) // this.$options.sockets['giveThing'] = data => { // if(this._uid == data.clientId && data.funCounter == funCounter){ // (this.$set || local.vue.Vue.set)(list, i.toString(), data.thing) // (this.$set || local.vue.Vue.set)(list.mapped, list[i][keyProperty], list[i]) // } // if(i==list.length-1){ // if((returnExistant && getsmart.bind(this)(list, 'mapped.'+returnExistant, false)) || !returnExistant){ // resolve(true) // } else if(returnExistant) { // resolve(false) // } else { // resolve() // } // } // } // } else if (i == list.length - 1) { if ( (returnExistant && getsmart.bind(this)(list, 'mapped.' + returnExistant, false)) || !returnExistant ) { resolve(true) } else if (returnExistant) { resolve(false) } else { resolve() } } } // if(list.mapped && !list.mapped['agora-client-mapped']){ // (this.$set || local.vue.Vue.set)(list.mapped, 'agora-client-mapped', true) // } } }) } export const domval = thing => { return getsmart.bind(this)(thing, 'properties.description', '') } export const getParent = (levels = Infinity) => { if (typeof levels == 'string') levels = (levels.match(/\.\./g) || []).length if (levels >= this.pathAsArray.length - 1) { return this.pathAsArray[0] } let level = this.pathAsArray.length - 1 - levels return this.pathAsArray[level] } export const getThing = (props = {}) => { const { list = getsmart.bind(this)(objList), defaultValue = undefined } = props var index = thingIn({ ...props, retIndex: true }) if (index >= 0) { return list[index] } else { return defaultValue } } export const equal = (obj1, obj2, seen = []) => { if (obj1 && obj2 && typeof obj1 == 'object' && typeof obj2 == 'object') { seen.push(obj1, obj2) //Loop through properties in object 1 for (const p in obj1) { //Check property exists on both objects if ( typeof obj1.hasOwnProperty == 'function' && typeof obj2.hasOwnProperty == 'function' && Object.prototype.hasOwnProperty.call(obj1, p) !== Object.prototype.hasOwnProperty.call(obj2, p) ) return false switch (typeof obj1[p]) { //Deep compare objects case 'object': if (seen.indexOf(obj1[p]) < 0 && !equal(obj1[p], obj2[p], seen)) return false break //Compare function code case 'function': if ( typeof obj2[p] == 'undefined' || obj1[p].toString() != obj2[p].toString() ) return false break //Compare values default: if (obj1[p] != obj2[p]) return false } } //Check object 2 for any extra properties for (const p in obj2) { if (!(p in obj1)) return false } return true } } export const mergeall = (array, options) => { if (!Array.isArray(array)) { throw new Error('first argument should be an array') } return array.reduce(function (prev, next) { return merge(prev, next, options) }, {}) } // export const smarts with all other exports export const smarts = { local, t, getBabel, uuid, pause, save, toJavascript, toJavascriptAux, createObjectProperties, serialize, stringify, replacer, setKnown, stringifier, primitives, Primitives, play, load, parse, parser, revive, createScopedEval, defineVariable, scopedEval, jsUUID, context, contextObject, createContext, createInlineContext, addBindingsToContext, scopeVarCode, scopeVarInlineCode, scopeVar, functionWrapper, bodyInsert, initBlock, getNodeUUID, getPathUUID, babelPlugin, transform, stripUuids, dupe, clone, schema, create, merge, basic, mod, deepForEach, forEachObject, forEachArray, setThing, setThings, optIn, thingIn, optsIn, thingsIn, anyOptsIn, anyThingsIn, optIndex, thingIndex, pushOpt, addOpt, pushThing, pushOpts, pushThings, popOpt, popThing, popOpts, popThings, toggleOpt, toggleThing, toggleOpts, toggleThings, // ratchetOpt, getsmart, escapePropertyPath, epp, escapeEscapes, ee, parsePropertyPath, ppp, parsePropertyArray, ppa, pathToArray, pathToString, setsmart, deletesmart, pushSmart, gosmart, gosmarter, absoluteType, vgosmart, getsmartval, safestring, safeparse, flatten, mapsmart, domval, getParent, getThing, equal, mergeall }