0

I have a simple javascript/jquery code which requests a data from some web service which returns the data which can be used to create a highcharts bar chart. The response from the server can't be parsed into JSON as it contains a click event handler which fails to be parsed by JSON.parse with error Unexpected keyword ....

The javascript code looks like this

$.ajax({
    type:"POST",
    url:"service/call"
}).done(function( xdata ) {
  // this is not going to work as xdata is not object but plain text
  $('#container').highcharts(xdata);
});

The response from the server is like

{
 "chart" : {
   "type" : "bar"
 },
 "series" : [ {
   "data" : [ 25, 10 ]
 } ],
 "title" : {
   "text" : ""
 },
 "xAxis" : [ {
   "categories" : [ "data1", "data2"],
  "allowDecimals" : false
 } ],
 "yAxis" : [ {
   "title" : {
     "align" : "high",
     "text" : "Some Title"
   },
   "allowDecimals" : false,
   "labels" : {
     "overflow" : "justify"
   },
   "min" : 0
 } ],
 "credits" : {
   "enabled" : false
 },
 "plotOptions" : {
   "bar" : {
     "colors" : [ "#87bdee", "#ffcccc"],
     "colorByPoint" : true,
     "dataLabels" : {
       "enabled" : true
     },
  "point" : {
       "events" : {
         "click" : function(){window.location.href = '/data?type=' + (this.x == 0 ? 'data1' : (this.x == 1 ? 'hold' : (this.x == 2 ? 'data2' : (this.x == 3 ? 'data3' : (this.x == 4 ? 'data4' : (this.x == 5 ? 'data5' : (this.x == 6 ? 'data6' : 'data7')))))) )}
       }
     }
   }
 },
 "tooltip" : {
   "valueSuffix" : " elements"
 },
 "creditOptions" : {
   "enabled" : false
 }
}

I have access to both server and client side code.

So is there a easy way a making things work ? I mean how can I create the highchart without changing the response ?

Babl
  • 7,446
  • 26
  • 37
  • 2
    It's trying to parse that response as JSON, but it's *not* JSON. You're *not* returning JSON. [JSON is a specific format](http://json.org/) that only supports strings, numbers, objects, arrays, booleans and null. You cannot have a `function` inside of a JSON file/string. – gen_Eric Dec 05 '16 at 20:03
  • You can try converting the function to a string, then parsing the function as string using `eval()` or `Function` – guest271314 Dec 05 '16 at 20:04
  • 1
    You can't have functions in JSON, it's purely a data format. You could have a string and run EVAL on it, or just have the function name and parameters (i.e keep the function defined on the client already, and just supply the parameters) – Dave Chen Dec 05 '16 at 20:04
  • @RocketHazmat yep I know that, so I want to know is there a easy way somehow to create the chart using this response which I get from the server :) – Babl Dec 05 '16 at 20:04
  • Updated the question, just removed all the things about JSON, so I have a service which returns some data which needs to be used to create a highchart ... – Babl Dec 05 '16 at 20:17
  • @Babl See my answer. – Scott Marcus Dec 05 '16 at 20:31
  • @ScottMarcus I'm on it ... :) thanks, will come back with solution if it works ... :) – Babl Dec 05 '16 at 20:35

2 Answers2

2

This is what I have done in the past.

<script type="text/javascript">

   function highChartOnClick() {
       alert('the click worked');
       window.location.href = '/data?type=' + (this.x == 0 ? 'data1' : (this.x == 1 ? 'hold' : (this.x == 2 ? 'data2' : (this.x == 3 ? 'data3' : (this.x == 4 ? 'data4' : (this.x == 5 ? 'data5' : (this.x == 6 ? 'data6' : 'data7')))))) );
   }

    $.ajax({
        type:"POST",
        url:"service/call"
    }).done(function( xdata ) {
         var someConfigurationJSONObject = xdata;
        someConfigurationJSONObject['plotOptions']['bar']['point']['events']['click'] = highChartOnClick;

         $('#container').highcharts(someConfigurationJSONObject);
    });
</script>
bassxzero
  • 4,838
  • 22
  • 34
  • This solution is good when on client side you definitely know what the function should do, but in my case the function is something which is automatically generated at server side, so there is no way to have something like this. And I can't create a separate web service call just to get the functions .... But anyway thanks for help .... – Babl Dec 06 '16 at 07:08
2

You can indeed turn a function stored/passed as a string into callable code if you use JSON.stringify with a "replacer" function and JSON.parse with a "reviver" function along with new Function()

See this previous post of mine for a complete working example.

Community
  • 1
  • 1
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 1
    I'm actually super conflicted about this answer. On one hand you have like 6 times the reputation that I do so I feel like you prolly know best, on the other this solution seems like cancer. So you dynamically create functions and give them their bodies? Again you have like 6 times my score, but are there any XSS problems with this? If not then i'm sad I didn't think of this. – bassxzero Dec 05 '16 at 20:32
  • Its an awful hack but its an awful hack that does what Babl requested. I can't think of a better way. – Rocky Sims Dec 05 '16 at 20:35
  • @bassxzero The ability to do what I'm showing is standard in the JSON object built into all modern browsers. I am not providing any commentary on "should you pass functions over HTTP and consume them in web clients". I am providing you with a perfectly legal and valid answer to your question. By the way, this solution is 10x better than `eval()` (as was suggested by others) because here, YOU control the reviver function, so you can scrub it for malicious content according to your own logic. – Scott Marcus Dec 05 '16 at 20:36
  • What I like in @ScottMarcus solution is that it needs no server side change. – Babl Dec 05 '16 at 20:40
  • And, if you do control the server code, then you control the HTTP Response payload, so XSS shouldn't be an issue. – Scott Marcus Dec 05 '16 at 20:41
  • 1
    @Babl Well, it actually does. The stringification takes place on the server and so the `JSON.stringify(obj, replacer)` would need to be on the server. – Scott Marcus Dec 05 '16 at 20:42
  • Hmmm I think I like it. – bassxzero Dec 05 '16 at 20:46