I have a config.js file which I believe is JSON which is called when the application first starts:
var config={};
config.user = [
{id:'JSMITH', priceModify:'true'},
{id:'JBLOGGS', priceModify:'false'},
]
config.price = [
{id:"price01", name:"priceName01", primary:"57.25", secondary:"34.54"},
{id:"price02", name:"priceName02", primary:"98.26", secondary:"139.45"},
{id:"price03", name:"priceName03", primary:"13.87", secondary:"29.13"}
]
To pull / push data I just use the following:
// Read
var curPrice = config.price[0].primary;
// Write
config.price[0].primary = "98.24";
How do I go about exporting the config file with the new value so that it will load next time the application is opened? I can use the file system object to write the file, I just don't understand how I would export everything (and preferably keep the same format).
I originally thought about reading the whole config file into a variable, cycling through to find the required block, id, and key and replacing the value, then writing the whole thing back, but I can't seem to figure out how to replace that specific value only.
Any help would be greatly appreciated
Edit Apologies, I forgot to mention that this application is completely offline and uses local directories
Solution
I stumbled across a few solutions to different issues which, when combined, gave me the perfect solution. First we cycle the Javascript object, building an array of the detail and then converting the array to a string:
vMethod.convertToText = function(obj) {
var string = [];
var output = '';
var count= 0;
var countTotal = 0;
if (typeof(obj) == "object" && (obj.join == undefined)) {
count= 0;
countTotal = 0;
string.push("{");
for (prop in obj) {
countTotal++;
}
for (prop in obj) {
if(count==countTotal - 1) {
string.push(prop, ": ", vMethod.convertToText(obj[prop]),'}\r\n');
} else {
string.push(prop, ": ", vMethod.convertToText(obj[prop]), ",");
}
count++;
};
} else if (typeof(obj) == "object" && !(obj.join == undefined)) {
count= 0;
countTotal = 0;
string.push("[\r\n")
for (prop in obj) {
countTotal++;
}
for(prop in obj) {
if(count==countTotal - 1) {
string.push(vMethod.convertToText(obj[prop]),'];\r\n');
} else {
string.push(vMethod.convertToText(obj[prop]), ",");
}
count++;
}
} else if (typeof(obj) == "function") {
string.push(obj.toString())
} else {
string.push(JSON.stringify(obj))
}
output = string.join("").toString();
//output = output.slice(1, -1);
return output;
}
Then we clean the array (neccessary for me to remove excess characters)
vMethod.cleanConfigText = function() {
var outputText = vMethod.convertToText(config);
outputText = outputText.slice(1, -1);
outputText = 'var config = {};\r\n'+outputText;
outputText = outputText.replace('user:','config.user =');
outputText = outputText.replace(',price:','config.price =');
outputText = outputText.slice(0, -2);
outputText = outputText.replace(/"/g, "'")
return outputText;
}
Finally a function to export the object into my config.js file:
vMethod.writeToConfig = function() {
vObject.fileSystem = new ActiveXObject('Scripting.FileSystemObject');
vObject.fileSystemFile = vObject.fileSystem.CreateTextFile('source\\js\\config.js',true);
vObject.fileSystemFile.Write(vMethod.cleanConfigText());
vObject.fileSystemFile.Close();
delete vObject.fileSystemFile;
delete vObject.fileSystem;
}
So when I want to export a change in the config, I just call:
vMethod.writeToConfig();
The only difference in the file format is that the commas appear at the start of a trailing line rather than the end of a preceding line but I can live with that!
Edit Turns out I'm anally retentive and the commas were bugging me
Added these to the clean up function and now the config is identical to before but without the indent
outputText = outputText.replace(/[\n\r]/g, '_');
outputText = outputText.replace(/__,/g, ',\r\n');
outputText = outputText.replace(/__/g, '\r\n');
Thank you to those that looked at the question and tried to help, very much appreciated.
Edit
DO NOT READ THE SOLUTION ABOVE, IT IS IN THE WRONG PLACE AND THERFORE IS NOT A VALID ANSWER. YOU'VE BEEN WARNED.
You can use a very popular npm package: https://www.npmjs.com/package/jsonfile . There are many but I've choosen this one.
Usually config stuff should be in json or .env files.
Now, all you have to do is use jsonfile's API to read/write JSON and parse (the package does the serialization/deserialization) it at the beginning when the application starts.
Example:
var jsonfile = require('jsonfile');
var util = require('util');
var config = null;
var file = './config.json';
// Reading
jsonfile.readFile(file, function(err, obj) {
config = obj;
});
// Writing
// Edit your config blah blah
config.user = [
{id:'JSMITH', priceModify:'true'},
{id:'JBLOGGS', priceModify:'false'},
];
config.price = [
{id:"price01", name:"priceName01", primary:"57.25", secondary:"34.54"},
{id:"price02", name:"priceName02", primary:"98.26", secondary:"139.45"},
{id:"price03", name:"priceName03", primary:"13.87", secondary:"29.13"}
];
jsonfile.writeFile(file, config, function (err) {
if(err) return err;
console.log('Config saved to file!');
});
Related
I have a script but it doesn't do what I need, so I need to adjust it, but honestly, I can't find what I need to change to make it work. The script outputs a CSV file from a folder of images. It makes a list with the file path so my program knows what images to import which works fine, but also it it writes the filenames which I can then also import in my secondary program.
The problem is the filenames. It also puts the extension behind the names which I don't need in the name. What do I need to change in the code to make it work as I want it to?
Here is the script:
Array.prototype.inArray = function(obj){
var arrMax = this.length-1;
for(var i=arrMax; i>=0; i--){
if(this[i]===obj){
return true;
}
}
return false;
}
var csvParser = (function(){
var csvFile;
return{
create:function(fo){
csvFile=File(fo+"/"+fo.name+".csv");
},
write:function(csvContent){
csvFile.open('w');
csvFile.encoding="UTF-8";
csvFile.write(csvContent);
csvFile.close();
},
execute:function(){
csvFile.execute();
},
getCSV:function(){
return csvFile;
}
}
})();
function imagesToCSVthenChoose(){
var doc,
fo,
fis,
fiMax,
fi,
fiName,
fiPath,
imgFormats=["eps","jpg","tif","psd","pdf","png","ai","bmp","jpeg"],
imgFormatMax = imgFormats.length-1,
imgOk = [],
csvContent = [],
csvLine=[],
csvSep=",";
if(app.documents.length==0){
alert("No documents open !");
return
}
doc=app.activeDocument;
fo = Folder.selectDialog("Please choose a folder with images");
if(!fo) return
fis = fo.getFiles();
fiMax=fis.length;
for(var i=0; i<fiMax; i++){
fi=fis[i];
ext = fi.name.match(/\.([a-z]+)$/i);
if(ext==null) continue;
ext = ext[1].toLowerCase();
if(!imgFormats.inArray(ext)) continue;
fiName = decodeURI(fi.name);
fiPath=decodeURI(fi.fsName);
csvContent.push(fiName+csvSep+fiPath);
}
csvContent = "Name"+csvSep+"#images\r"+csvContent.join("\r");
csvParser.create(fo);
csvParser.write(csvContent);
/*
doc.dataMergeProperties.selectDataSource(csvParser.getCSV());
var myMenuAction = app.menuActions.item("$ID/DataMergePanelName");
myMenuAction.invoke();
*/
}
imagesToCSVthenChoose();
I didn't try much yet because I have no clue. And I couldn't find the right code on the internet.
I'm trying to write a script that allows me to search for a string in a number of folders and then return the output. I've managed to find the readdirSync function which does what I want, however I am unable to print out the total number of strings found because it is ran before the function is complete. Please see example below.
var totalNumberOfStringFound = 0;
function numberofStringsInFolder(dir, search) {
var fs = require('fs');
var results = [];
var searchTerm = search;
fs.readdirSync(dir).forEach(function (file) {
file = dir + '/' + file;
var stat = fs.statSync(file);
if (stat && stat.isDirectory()) {
results = results.concat(numberOfTagsInFolder(file));
} else {
fs.readFile(file, bar);
function bar(err, data) {
err ? Function('error', 'throw error')(err) : (fileContent = data.toString('utf8'));
var count = (fileContent.match(new RegExp(searchTerm, 'gi')) || []).length;
totalNumberOfStringFound = totalNumberOfStringFound + count;
console.log('NUMBER OF STRINGS FOUND: ' + totalNumberOfStringFound);
}
// Holds the list of files found
results.push(file);
}
});
console.log("totalNumberOfStringFound: " + totalNumberOfStringFound);
return results;
}
numberofStringsInFolder('./folder', 'HELLO');
Output
totalNumberOfStringFound: 0
NUMBER OF STRINGS FOUND: 2
NUMBER OF STRINGS FOUND: 3
The output (totalNumberOfStringFound) should be 3, but because totalNumberOfStringFound is called before the function has finished, it is showing as 0. I've looked online and some people use the timeout function, but I don't want to use that because I don't know how long it will take to complete. I would really appreciate it if someone can help. Thank you
Trying to build typescript code in JS so i can display on UI for user to play with the code , is there a way to create this code as a typescript instead of text so it compile as well ? Any help here will be highly appreciated couldn't find any source related to this issue.
data contains interfaces
main.js
function buildTypescript(data) {
var _ref = window.activeOperation;
var modelData = getModelData(data);
var text = '';
text += "import {Api as IngenSDK} from '#SSDK'" + ';\n\n';
text += modelData;
text += 'app.Api.setConfig({\n "env": "SIT3"\n});\n\n';
text += _ref + '(' + JSON.stringify(SDKresult[_ref].request, null, 4) + ', function(result) {\n //Your code goes here \n debugger; \n console.log(result); \n});';
$('#request_method_TS').text(text);
}
function getModelData(data){
var activePath = window.activePath.toLowerCase();
var _interface;
$.each(data.children, function(id, item){
// item.name would be string like "#SDK/core/interface/member/Details";
var path = item.name.replace(/\"/g, "");
if (path.toLowerCase().includes(activePath)) {
console.log('OBJ', item);
_interface = createInterfaces(path,item.children);
}
});
return _interface;
}
function createInterfaces(path, data) {
const imports = data.map(d => d.name).join(', ');
return `import { ${imports} } from '${path}';\n\n`;
}
html
<pre id="request_method_TS" style="margin: 5px;"></pre>
You can create the Typescript as executable by doing something like -
const executableTypescript = new Function(typescriptDataAsText);
The typescript code in the string will already be compiled at this point.
To execute it, you just call the newly created function like -
executableTypescript();
Check by running the below snippet that the Typescript code of logging the message in the variable is executed.
Note: For your case, if the modelData and more such Typescript data is included, it should be a part of a new module since it has import statements and they are required to be on top of a module.
var path = "/filepath"
var data = [
{
name: "IParam"
},
{
name: "IError"
}
]
function createInterfaces(path, data){
const imports = data.map(d => d.name).join(', ');
return `import { ${imports} } from '${path}';\n\n`;
}
function buildTypescript(data) {
// To include this data as part of your text, make sure that it's a part of a new module
const modelData = createInterfaces(path,data);
const text = `
let message = "Typecript executed";\n\n
console.log(message)`;
return text;
}
const typescriptDataAsText = buildTypescript(data);
const executableTypescript = new Function(typescriptDataAsText);
executableTypescript()
I am looking for a JavaScript library that parses an XML string and converts it to a JavaScript object. What are some good ones?
The following function parses XML and returns a JavaScript object with a scheme that corresponds to the XML. XML siblings w/ the same name are collapsed into arrays. nodes with names that can be found in the arrayTags parameter (array of tag name strings) always yield arrays even in case of only one tag occurrence. arrayTags can be omitted. Text nodes with only spaces are discarded.
function parseXml(xml, arrayTags) {
let dom = null;
if (window.DOMParser) dom = (new DOMParser()).parseFromString(xml, "text/xml");
else if (window.ActiveXObject) {
dom = new ActiveXObject('Microsoft.XMLDOM');
dom.async = false;
if (!dom.loadXML(xml)) throw dom.parseError.reason + " " + dom.parseError.srcText;
}
else throw new Error("cannot parse xml string!");
function parseNode(xmlNode, result) {
if (xmlNode.nodeName == "#text") {
let v = xmlNode.nodeValue;
if (v.trim()) result['#text'] = v;
return;
}
let jsonNode = {},
existing = result[xmlNode.nodeName];
if (existing) {
if (!Array.isArray(existing)) result[xmlNode.nodeName] = [existing, jsonNode];
else result[xmlNode.nodeName].push(jsonNode);
}
else {
if (arrayTags && arrayTags.indexOf(xmlNode.nodeName) != -1) result[xmlNode.nodeName] = [jsonNode];
else result[xmlNode.nodeName] = jsonNode;
}
if (xmlNode.attributes) for (let attribute of xmlNode.attributes) jsonNode[attribute.nodeName] = attribute.nodeValue;
for (let node of xmlNode.childNodes) parseNode(node, jsonNode);
}
let result = {};
for (let node of dom.childNodes) parseNode(node, result);
return result;
}
Here's a nice xml2json and json2xml converter:
http://goessner.net/download/prj/jsonxml/
Related tutorial: http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html
Here's another one:
http://www.kawa.net/works/js/xml/objtree-e.html
Depending on your needs, you might be able to use a standard parser (see http://www.w3schools.com/XML/tryit.asp?filename=tryxml_parsertest2) and xpath (http://www.w3schools.com/xpath/default.asp) - here's an example:
http://snippets.dzone.com/posts/show/5272
and a few nice tutorials:
http://www.nczonline.net/blog/2009/03/17/xpath-in-javascript-part-1/
https://developer.mozilla.org/en/introduction_to_using_xpath_in_javascript
Going straight to the point (using node-xml2json):
npm install xml2json
Then, use it:
const parser = require('xml2json');
const obj = parser.toJson(xml, { object: true });
Example:
const parser = require('xml2json');
const xml = '<root><person><name>Bob Dylan</name></person></root>';
const obj = parser.toJson(xml, { object: true });
const { person } = obj.root;
person.name; // Bob Dylan
You can also convert from JSON to XML, and much more.
I wanted a simple Typescript version that didn't create additional #text objects and also disregarded attributes. If that's what you need, here's the code:
export class DomFuncs {
static parseNode = (node: Node) => {
const childNodes = node.childNodes;
if (childNodes.length === 0) {
return node.nodeValue;
} else if (childNodes.length === 1 && childNodes[0].nodeType === Node.TEXT_NODE) {
return childNodes[0].nodeValue;
} else {
const obj = {};
childNodes.forEach(childNode => {
const childName = childNode.nodeName;
const childValue = obj[childName];
if (childValue !== undefined) {
if (Array.isArray(childValue)) {
childValue.push(DomFuncs.parseNode(childNode));
} else {
obj[childName] = [childValue, DomFuncs.parseNode(childNode)];
}
} else {
obj[childName] = DomFuncs.parseNode(childNode);
}
});
return obj;
}
};
static xml2obj = (str: string) => {
const dom = (new DOMParser()).parseFromString(str, 'text/xml')
const result = {[dom.nodeName]: DomFuncs.parseNode(dom)};
return result;
}
}
To use it:
DomFuncs.xml2obj(xmlString);
This script currently disregards XML attributes since my converted object didn't require them. If you need that, let me know and I could update the code.
The xml2json javascript file from https://bitbucket.org/surenrao/xml2json is all you need to do this.
Here's the download link for quick download: https://bitbucket.org/surenrao/xml2json/get/0e0989dfe48e.zip
Once included in your project, here's some sample code to get you started:
var xmlStr = "<root><person><name>Bob Dylan</name></person></root>";
var jsObj = X2J.parseXml(xmlStr);
var result = jsObj[0].root[0].person[0].name[0].jValue; //Bob Dylan
I have some filters:
var jade = require('jade');
jade.filters.Posts = function(block) {
return '{block:Posts}'+jade.render(block)+'{/block:Posts}';
};
jade.filters.Audio = function(block) {
return '{block:Audio}'+jade.render(block)+'{/block:Audio}';
};
jade.filters.Video = function(block) {
return '{block:Video}'+jade.render(block)+'{/block:Video}';
};
And have some input
:Posts
Posts
:Audio
| Audio
:Video
| Video
So I have an error:
>> unknown filter ":Audio"
Can I handle or fix this problem?
PS You can look at the code in this repository — I'm using grunt and grunt-contrib-jade plugin, but to force grunt-contrib-jade work with filters you should edit ./node_modules/grunt-contrib-jade/tasks/jade.js to reflect changes from this pull request.
PS2: I found the stumbling block. When I use render() method inside filter, I invoke it from local jade instance, which is don't know anything about filters, but global jade instance (from Gruntfile.js) have all information about that filters. That's why the main question is: how can I throw global Jade-instance to file with filters?
PS3: I don't know how create fiddle for such case. But you can clone my Hampi repo, implement changes to grunt-contrib-jade from my PR to them, then at start run npm i. To compile templates run grunt jade. Pay attention to these line in body.jade and commented section in filters.
PS4. I find the reason and it in different scope. I describe it with details here. Can you solve this issue?
I'm open to additional answers and I will accept fixes in jade core (if it would be required).
We just should bind global jade instance to filters like this:
var jade = require('jade');
if (options.filters) {
// have custom filters
Object.keys(options.filters).forEach(function(filter) {
if (_.isFunction(data)) {
// have custom options
jade.filters[filter] = options.filters[filter].bind({jade: jade, locals: data()});
} else {
// have no custom options
jade.filters[filter] = options.filters[filter].bind({jade: jade });
}
});
}
See implementation here — in this commit
I think you are right at problem place, the problem is in the filter.js file
location jade/lib/filters.js
var transformers = require('transformers');
module.exports = filter;
function filter(name, str, options) {
if (typeof filter[name] === 'function') {
var res = filter[name](str, options);
} else if (transformers[name]) {
var res = transformers[name].renderSync(str, options);
if (transformers[name].outputFormat === 'js') {
res = '<script type="text/javascript">\n' + res + '</script>';
} else if (transformers[name].outputFormat === 'css') {
res = '<style type="text/css">' + res + '</style>';
} else if (transformers[name].outputFormat === 'xml') {
res = res.replace(/'/g, ''');
}
} else {
throw new Error('unknown filter ":' + name + '"');
}
return res; // returns RES that is not defined in scope of function.
}
filter.exists = function (name, str, options) {
return typeof filter[name] === 'function' || transformers[name];
};
Here I have identified one flaw that you can correct like this,
var transformers = require('transformers');
module.exports = filter;
function filter(name, str, options) {
var res;//defined a variable which is global to the scope of function.
if (typeof filter[name] === 'function') {
res = filter[name](str, options);
} else if (transformers[name]) {
res = transformers[name].renderSync(str, options);
if (transformers[name].outputFormat === 'js') {
res = '<script type="text/javascript">\n' + res + '</script>';
} else if (transformers[name].outputFormat === 'css') {
res = '<style type="text/css">' + res + '</style>';
} else if (transformers[name].outputFormat === 'xml') {
res = res.replace(/'/g, ''');
}
} else {
throw new Error('unknown filter ":' + name + '"');
}
return res;
}
filter.exists = function (name, str, options) {
return typeof filter[name] === 'function' || transformers[name];
};
It may be possible that nesting under some function makes audio function out of scope. Does audio function works alone!?
although there may be other things if the problem not solved, please create a fiddle for your for better understanding.