How to integrate syntax check in Ace Editor using custom mode? - jshint

I'm new to ace-editor and I have included custom mode to validate my code and every line should end up with semicolon, If semicolon is not present in my query by mistake then the editor should gives up the warning like "Missing Semicolon".
define('ace/mode/javascript-custom', [], function(require, exports, module) {
var oop = require("ace/lib/oop");
var TextMode = require("ace/mode/text").Mode;
var Tokenizer = require("ace/tokenizer").Tokenizer;
var ExampleHighlightRules = require("ace/mode/example_highlight_rules").ExampleHighlightRules;
var Mode = function() {
this.HighlightRules = ExampleHighlightRules;
};
oop.inherits(Mode, TextMode);
(function() {
this.lineCommentStart = "--";
this.blockComment = {
start: "->",
end: "<-"
};
}).call(Mode.prototype);
exports.Mode = Mode;
});
define('ace/mode/example_highlight_rules', [], function(require, exports, module) {
var oop = require("ace/lib/oop");
var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
var ExampleHighlightRules = function() {
var keywordMapper = this.createKeywordMapper({
"variable.language": "this",
"keyword": "one|two",
"constant.language": "true|false|null"
}, "text", true);
this.$rules = {
"start": [{
token: "comment",
regex: "->",
next: [{
regex: "<-",
token: "comment",
next: "start"
}, {
defaultToken: "comment"
}]
}, {
regex: "\\w+\\b",
token: keywordMapper
}, {
token: "comment",
regex: "--.*"
}, {
token: "string",
regex: '"',
next: [{
regex: /\\./,
token: "escape.character"
}, {
regex: '"',
token: "string",
next: "start"
}, {
defaultToken: "string"
}]
}, {
token: "numbers",
regex: /\d+(?:[.](\d)*)?|[.]\d+/
}]
};
this.normalizeRules()
};
oop.inherits(ExampleHighlightRules, TextHighlightRules);
exports.ExampleHighlightRules = ExampleHighlightRules;
});
var langTools = ace.require("ace/ext/language_tools");
var editor = ace.edit("editor");
editor.session.setMode("ace/mode/javascript-custom");
editor.setOptions({
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
});
editor.setTheme("ace/theme/monokai");
var lines = editor.session.doc.getAllLines();
var errors = [];
for (var i = 0; i < lines.length; i++) {
if (/[\w\d{(['"]/.test(lines[i])) {
alert("hello");
errors.push({
row: i,
column: lines[i].length,
text: "Missing Semicolon",
type: "error"
});
}
}
<script src="https://ajaxorg.github.io/ace-builds/src/ext-language_tools.js"></script>
<script src="https://ajaxorg.github.io/ace-builds/src/ace.js"></script>
<div id="editor" style="height: 200px; width: 400px"></div>
<div id="commandline" style="position: absolute; bottom: 10px; height: 20px; width: 800px;"></div>
UPDATE:
The following js files are generated from ace and added to my rails application, the files are loaded in rails app but the functionality (semicolon check) doesn't seem to be working.
worker-semicolonlineend - http://pastebin.com/2kZ2fYr9
mode-semicolonlineend - http://pastebin.com/eBY5VvNK
Update:
In ace editor, type in a query1, query2 in line 1 and line 2 respectively
Leave the third line blank
Now in fourth line, type a query without semicolon in the end, x mark appears in third line
5 And when the fifth line is also without a semicolon, then the x mark is displayed at fourth query

Ace editor widely support this kind analysis for JavaScript by default:
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
<div id="editor">function foo() { ; // unnessesary semicolon
var x = "bar" // missing semicolon
return x; // semicolon in place
}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
</script>
Just make sure that worker file worker-javascript.js is available for your code. In code snippet above I use CDN to get Ace build, so worker is always available. You can configure JSHint via worker options.
Update: But if really need something beyond that you will need to do the following as my understanding goes:
Create Worker and Mode for you kind of analysis
Download Ace source code and install NodeJS
Put your new files within correspond Ace source code folders
Build Ace
Add build files to your project
Use new mode: editor.getSession().setMode("ace/mode/semicolonlineend");
Worker that perform line ending check will look something like that:
define(function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
var Mirror = require("../worker/mirror").Mirror;
var SemicolonLineEndCheckWorker = exports.SemicolonLineEndCheckWorker = function (sender) {
Mirror.call(this, sender);
this.setTimeout(500);
this.setOptions();
};
oop.inherits(SemicolonLineEndCheckWorker, Mirror);
(function() {
this.onUpdate = function () {
var text = this.doc.getValue();
var lines = text.replace(/^#!.*\n/, "\n").match(/[^\r\n]+/g);
var errors = [];
for (var i = 0; i < lines.length; i++) {
var lastLineCharacter = lines[i].trim().slice(-1);
if (lastLineCharacter === ';')
continue;
errors.push({
row: i,
column: lines[i].length-1,
text: "Missing semicolon at the end of the line",
type: "warning",
raw: "Missing semicolon"
});
}
this.sender.emit("annotate", errors);
};
}).call(SemicolonLineEndCheckWorker.prototype);
});
New mode that uses worker:
define(function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var Mode = function() { };
oop.inherits(Mode, TextMode);
(function() {
this.createWorker = function(session) {
var worker = new WorkerClient(["ace"], "ace/mode/semicolonlineend_worker",
"SemicolonLineEndCheckWorker");
worker.attachToDocument(session.getDocument());
worker.on("annotate", function(results) {
session.setAnnotations(results.data);
});
worker.on("terminate", function() {
session.clearAnnotations();
});
return worker;
};
this.$id = "ace/mode/semicolonlineend";
}).call(Mode.prototype);
exports.Mode = Mode;
});

Related

When to use ReactiveDict vs ReactiveVar

I'm in Meteor and have been using Reactive Var in my project with no issues. In this current example I'm not sure if I need to use a ReactiveDict instead because I'm getting no reactivity with ReactiveVar.
I'm setting the value of the reactiveVar to a mongo query that is resulting in an array of objects.
Template.MasterList.onCreated(function () {
this.teacherList = new ReactiveVar('');
});
Template.MasterList.onRendered(function () {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var query1 = {};
query1[week] = { $in: ['JC', 'JF', 'F']};
this.teacherList.set(Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, query1]}, { sort: { FullName: 1 }}).fetch());
});
One object in the array would look like:
{ ...
Students: {
Week1: {
Monday: ['Joe', 'Mary']
},
Week2: {},
Week3: {}
}
...
}
One of the objects in the array may change to something like:
{ ...
Students: {
Week1: {
Monday: ['Joe', 'Mary', 'Bill']
},
Week2: {},
Week3: {}
}
...
}
When a change to one of the objects happens, the reactiveVar doesn't see the change in the array to re-fire the helper. Is a ReactiveDict to be used here instead?
EDIT:
Helper function:
saturdayTeacher: function (name) {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var teachers = Template.instance().teacherList.get();
for (var i = 0, len = teachers.length; i < len; i++) {
if (_.contains(teachers[i].Students[week].Saturday, name)) {
Meteor.defer(function () {
i = 0;
});
var teacher = teachers[i].FullName.split(' ');
return teacher[0] + " " + teacher[1].slice(0, 1);
}
}
},
What will help you in a very easy way, is to add an autorun to your onRendered-Function, where you set your ReactiveVar. Because the onRendered-Function is only executed once. When I get it right, you want to listen on changes in the cursor, Session.get('CurrentWeek') and Session.get('GlobalCurrentCampYear').
So try this:
Template.MasterList.onRendered(function () {
var self = this;
this.autorun(function() {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var query1 = {};
query1[week] = { $in: ['JC', 'JF', 'F']};
self.teacherList.set(Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, query1]}, { sort: { FullName: 1 }}).fetch());
});
});
Than it will be executed all the time the result of the query or the Session-Values change.

FullCalendar RefetchEvents very slow

I call calendar.refetchEvents(); inside an autorun Block to ensure the reactivity of the Scheduler (i'm using resource view) , when tested with big datasets , although i make sure to subscript to only a portion of 2 weeks worth of events , it's extremly slow .
my events arent Json based i'm using meteor and i loop over the events inside the Events function of the calendar .
are there some good fullcalendar practices i'm missing ?
calendar = $('#calendar').fullCalendar({
now: new Date(),
editable: true, // enable draggable events
droppable: true, // this allows things to be dropped onto the calendar
aspectRatio: 1.8,
disableDragging: true,
displayEventTime: false,
selectable:true,
allDaySlot:true,
slotDuration:'24:00',
lazyFetching:true,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineThreeDays'
},
defaultView: 'timelineThreeDays',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 14 }
}
},
eventAfterAllRender: function(){
Session.set("loading",false);
},
resourceLabelText: 'Employees',
eventRender: function (event, element) {
var originalClass = element[0].className;
if (event.id && event.id.indexOf("temp")>-1){
element[0].className = originalClass + ' dragEvent';
}
else if (event.id && event.id.indexOf("oloc")>-1){
element[0].className = originalClass + ' oloc';
}
else{
element[0].className = originalClass + ' hasmenu';
}
$(element[0]).attr('shift-id', event._id);
element.find('.fc-title').html((event.title?event.title+"<br/>":"")+(event.locationName?event.locationName+"<br/>":"")+moment(event.start).format("HH:mm")+ " "+moment(event.end).format("HH:mm"));
element.bind('mousedown', function (e) {
if (e.which == 3) {
Session.set("selectedShift",event._id);
}
});
},eventAfterRender: function(event, element, view) {
},
resourceRender: function(resourceObj, labelTds, bodyTds) {
var originalClass = labelTds[0].className;
var uid=resourceObj.id;
var resource = Meteor.users.findOne({_id:uid});
if(resource){
var img = Images.findOne({_id: resource.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + resource.profile.firstname + " " + resource.profile.lastname+" <small style='font-size:0.6em;color:#D24D57;'>"+resource.profile.registeredTelephony+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
labelTds[0].className = originalClass + ' hasResourceMenu';
}else{
var imgUrl = "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + "Unassigned" + " " +" <small style='font-size:0.6em;color:#D24D57;'>"+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
}
},
resources: function(callback) {
var users = [];
var data = Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
});
var arr = data.map(function(c) {
var employeeType = c.userSettings.employeeType;
var type = EmployeeType.findOne({_id:employeeType});
var img = Images.findOne({_id: c.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
c.name = c.name || "";
var totalHoursAllLocation = 0;
var totalHoursCurrentLocation = 0;
return {
id: c._id,
title: "t"
};
});
arr.push({id:"temp"+Session.get("companyId")+Session.get("locationId"),title:"<div class='img-profil small' style='background: url(/images/default-avatar.png);'></div> UnAssigned"});
callback(arr);
},
events: function(start, end, timezone, callback) {
},
drop: function(date, jsEvent, ui, resourceId) {
// retrieve the dropped element's stored Event Object
var locationId=Session.get("locationId");
var originalEventObject = $(this).data('eventObject');
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
//copiedEventObject.allDay = allDay;
shift = ShiftTypes.findOne({_id:copiedEventObject.id});
if(shift){
startDate = moment(date);
hour = shift.dayDuration.Start.split(":");
startDate.hours(hour[0]).minutes(hour[1]);
endDate = moment(date);
hour = shift.dayDuration.End.split(":");
endDate.hours(hour[0]).minutes(hour[1]);
if(moment(startDate).isAfter(endDate)){
endDate=endDate.add("1","days");
}
var data = {
shiftId:shift._id,
name:shift.name,
uid:resourceId,
locationId:Session.get("locationId"),
companyId:Session.get("companyId"),
day:date,start:startDate.utc().toDate(),
end:endDate.utc().toDate(),
type:"active"
};
if (SchedulesBeforeInsert(data,"dropActive")){
Schedules.insert(data,function (err,result) {});
}
}
},
eventResize: function( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
endDate = moment.utc(new Date(event.start));
schedule = Schedules.findOne({_id:event.id});
var delta = dayDelta._days;
for(i=1;i<delta+1;i++){
Schedules.insert({start:moment.utc(event.start).add(i,"days").toDate(),end:moment.utc(schedule.end).add(i,"days").toDate(),uid:schedule.uid,locationId:Session.get("locationId"),companyId:schedule.companyId,name:schedule.name,shiftId:schedule.shiftId,attendanceCode:schedule.attendanceCode,type:schedule.type});
}
},
dayClick: function(date, jsEvent, view,res,res2) {
Session.set("selectedDay",moment(date).toDate());
Session.set("selectedRessource",res.id);
},
eventClick: function ( event, jsEvent, view ) {
toastr.success(moment(event.start).format('HH:mm') +" TO "+moment(event.endDate).format('HH:mm'))
},
eventReceive: function(event) { // called when a proper external event is dropped
console.log('eventReceive', event);
}
}).data().fullCalendar;
/********************* reactive calendar *****************/
this.autorun(function() {
console.log("autoRun")
Meteor.subscribe("schedules",Session.get("companyId"),moment($('#calendar').fullCalendar('getDate')).startOf('week').toDate(),moment($('#calendar').fullCalendar('getDate')).add(4,"weeks").endOf('week').toDate());
var events = [];
var usersInLocation = Meteor.users.find({$or:[{"profile.showInScheduler":{$exists:false}},{"profile.showInScheduler":true}],assignedTo:{$in:[Session.get("locationId")]},'locations._id':Session.get("locationId"),"profile.companyId":Session.get("companyId")}).fetch();
var userIds = _.pluck(usersInLocation,"_id");
userIds.push("temp"+Session.get("companyId")+Session.get("locationId"));
var data;
if(Session.get("displayAllLocations")===true){
reqEvents = Schedules.find({uid:{$in:userIds},companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}else{
reqEvents = Schedules.find({uid:{$in:userIds},locationId:Session.get("locationId"),companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}
reqEvents.forEach(function(evt){
var event = null;
color="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode});
color=attendance.color;
}
attendance = null;
color="";
id="";
locationName="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode})
if(attendance){
color=attendance.color;
}
}
else if(evt.employeeAttendanceCode){
attendance =AttendanceCodesPortal.findOne({_id:evt.employeeAttendanceCode})
if(attendance){
color=attendance.color;
}
}
id=evt._id;
if(evt.locationId!==Session.get("locationId")){
color="#EEEEEE";
id="oloc";
location =Locations.findOne({_id:evt.locationId});
if(location){
locationName=location.name;
}
}
if(evt.name != null){
event = {id:id,title:evt.name,start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}else{
event = {id:id,title:" ",start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}
events.push(event);
});
allUsersCursor =Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
}).fetch();
if(calendar){
calendar.removeEvents();
calendar.addEventSource(events);
calendar.refetchResources();
//SetUp the actions context menu
setUpContextMenu();
// Initialize the external events
$('#external-events div.external-event').each(function() {
var eventObject = {
title: $.trim($(this).text()), // use the element's text as the event title
id:$(this).attr("id")
};
// store the Event Object in the DOM element so we can get to it later
$(this).data('eventObject', eventObject);
// make the event draggable using jQuery UI
$(this).draggable({
helper: 'clone',
revert: 'invalid',
appendTo: 'body'// original position after the drag
});
});
}
});
I can see a number of non-Meteoric patterns in there that you should optimize out:
Your this.autorun will rerun anytime anything changes in your Schedules collection as well as for various user object changes (not just to the logged-in users but other users as well). Your autorun rebuilds your client-side data from scratch every time, including numerous joins. You could use a local collection to cache the joined data and then only add/change those documents when the underlying persisted document changes. This can be done with an observeChanges pattern. Or you could denormalize your data model a bit to avoid all those joins.
You're modifying the DOM by attaching events to a number of selectors using jQuery instead of using Meteor event handlers. This is also happening in your autorun which means you're attaching the same events to the same DOM objects repeatedly.
You use many Session variables and you use them repeatedly. These can be slow since they use browser local storage. You should .get() such data into a local variable once only and then refer to the local variable from then on.
Make sure that you only include fields in your Schedules publication that your code actually refers to on the client. The rest are overhead.

Changing navigator data in iframe with SlimerJS (or PhantomJS)

By one trick or another I managed to handle headers in both new windows (window.open, target=blank etc) and iframes with SlimerJS. It is also possible to change navigator data (such as navigator.appVersion) for new windows, but I'm stuck with doing this for iframes. It looks like the onInitialized method works correcrtly only for main window and new windows, but not for iframes.
Here is some parts of the code.
var headers =
{
"Accept-Language" : "test_language",
"Accept" : "test/accpet",
"Connection" : "keep-alive",
"Keep-Alive" : "333",
"Accept-Charset" : "test-utf",
"User-Agent" : "test_ua"
}
var webpage = require('webpage').create(
{
pageSettings:
{
javascriptEnabled: true,
loadImages: true,
loadPlugins: true,
}
});
_onPageCreated = function(childPage)
{
console.log('a new window is opened');
childPage.settings.userAgent = options.useragent["navigator.userAgent"];
childPage.customHeaders = headers;
childPage.viewportSize = { width:400, height: 500 };
childPage.onInitialized = _onInitialized;
childPage.onNavigationRequested = _onNavigationRequested;
childPage.onLoadStarted = _onLoadStarted;
childPage.onLoadFinished = _onLoadFinished;
childPage.onResourceRequested = _onResourceRequested;
childPage.onPageCreated = _onPageCreated;
};
_onResourceRequested = function(requestData, networkRequest)
{
for(var h in headers)
{
networkRequest.setHeader(h, headers[h], false);
}
...
}
var _onInitialized = function()
{
this.customHeaders = headers;
console.log("[webpage.onInitialized]");
this.evaluate(function(options)
{
(function()
{
window.navigator.__defineGetter__('appVersion', function ()
{
return options.useragent["navigator.appVersion"];
});
...
})();
}, options);
};
...
webpage.onInitialized = _onInitialized;
webpage.onNavigationRequested = _onNavigationRequested;
webpage.onLoadFinished = _onLoadFinished;
webpage.onResourceRequested = _onResourceRequested;
webpage.onPageCreated = _onPageCreated;
I tried to do this with PhantomJS, but it seems like it is not possible to use "this" instead of "webpage" in the following case:
var _onInitialized = function()
{
console.log("[webpage.onInitialized]");
this.evaluate(function() // <<---not working
{
});
console.log("test"); //is not being printed
};
I would like this function to work with "this" object, so that it could be defined as onInitialized for both main page and child pages.
Anyway, the main question is about the SlimerJS code, not the Phantom one.

Replace all the text with specified replacement using grunt replace

I have a .html file which contains id="fixedtext", I want to replace all these id with id="uniquetext"
the grunt-text-replace just replaces the first id it finds and doesnot parse the entire text.
Any idea how can I make either grunt-text-replace https://github.com/yoniholmes/grunt-text-replace
or
grunt-replace https://www.npmjs.com/package/grunt-replace
to do this for the entire document and not just for the first occurrence.
replace: {
dist: {
options:{
patterns:[{
match:'id="fixedtext"',
replacement: 'id="'+something[i++] +'"'
}],
files:[
{
expand: true,
src:['./source.html'],
dest:'./dest.html'
}
]
}
}
},
this is what can be done if unique ids are to be added.
Assuming that you already have an array of all the id you want to add when you run your task.
In this case the id's are the file names
create your own wrapper task
var path = require('path');
var fs = require('fs');
grunt.initconfig({
wrap:{
html:{
header:'<script type="text/ng-template" ',
footer:'</script>',
src:'./yourPathToFile/',
dest'./yourPathToDest/'
}
}
});
grunt.registerMultiTask('wrap', 'wrap header and footer with custom id', function(){
var data = this.data;
getListOfFiles(data.src);
function getListOfFiles(expand_path){
var listOfFiles = fs.readdirSync(expand_path);
for(var i=0; i<listOfFiles.length; i++){
var completePath = expand_path + listOfFiles[i];
var extension = path.extname(completePath);
if(fs.lstatSync(completePath).isDirectory()){
var newDirPath = completePath + '/';
console.log('true------ : \n',newDirPath);
getListofFiles(newDirPath);
}
else if(extension == '.html'){
console.log('F:\n', completePath);
fullSrcPath = path.resolve(completePath);
content = grunt.file.read(fullSrcPath);
scriptId = 'id="' + listOfFiles[i]+'">';
header = (grunt.template.process(data.header));
footer = (grunt.template.process(data.footer));
wholeFile = header + scriptId + content + footer;
grunt.file.write(fullSrcPath, wholeFile);
}
}
}
});

AJAX Call using Enyo Framework

I am trying to make an ajax call using the enyo framework and I am running headlong in to a problem. The error message I am getting is 0. That's it just a 0. I made sure my link to the json file was correct and I built this jsfiddle to test it out http://jsfiddle.net/mmahon512/CPU8n/2/ Any help is greatly appreciated. My host is GoDaddy and I made sure that I added the json extension to my web config correctly. The link to the json file is correct and it returns valid json. I checked it using jsonlint. Here is what the code looks like on jsfiddle:
enyo.kind({
name: "AjaxSample",
components: [
{ kind: "Button", content: "Fetch Users", ontap: "fetch" },
{ name: "repos", content: "Not loaded...", allowHtml: true }
],
fetch: function() {
var ajax = new enyo.Ajax({
url: "http://atxapps.com/_sites/atxapps.com/dev/jetstream/assets/dataUsers.json"
});
ajax.go();
ajax.response(this, "gotResponse");
ajax.error(this, this.gotError);
},
gotResponse: function(inSender, inResponse) {
var output = "";
for(i = 0; i < inResponse.length; i++) {
output += inResponse[i].Id + "";
}
output += Date.now();
this.$.repos.setContent(output);
},
gotError: function(inSender, inError) {
alert(inError);
this.$.repos.setContent(inError + " " + Date.now());
}
});
Looks like a CORS issue. I see the following in the console:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin fiddle.jshell.net is therefore not allowed access.
I wrapped it as a jsonp request successfully.
http://jsfiddle.net/CPU8n/3/
enyo.kind({
name: "AjaxSample",
components: [
{ kind: "Button", content: "Fetch Users", ontap: "fetch" },
{ name: "repos", content: "Not loaded...", allowHtml: true }
],
fetch: function() {
var ajax = new enyo.JsonpRequest({
url: "http://jsonpwrapper.com/?urls%5B%5D=http%3A%2F%2Fatxapps.com%2F_sites%2Fatxapps.com%2Fdev%2Fjetstream%2Fassets%2FdataUsers.json"
});
ajax.go();
ajax.response(this, "gotResponse");
ajax.error(this, this.gotError);
},
gotResponse: function(inSender, inResponse) {
var output = "";
var body = enyo.json.parse(inResponse[0].body); // jsonpwrapper.com wraps the results in a array with an index for each URL. The actual data is in the body param of that index but it isn't parsed (at least in this example)
for(i = 0; i < body.length; i++) {
output += body[i].Id + "<br />";
}
output += Date.now();
this.$.repos.setContent(output);
},
gotError: function(inSender, inError) {
alert(inError);
this.$.repos.setContent(inError + " " + Date.now());
}
});
If you're running this on the same server in prod, you wouldn't see the error (since it's not cross-origin). If it'll be on a different server, you can either convert the server-side to support jsonp or adds the appropriate CORS headers.

Resources