I am absolute begginer in jQuery. I wrote some code for my app and put it into .js file:
How should I avoid code repetition in jQuery? Where should I store my .js code (one huge .js file, couple of smaller or straight in html source)?
This is my .js file:
$(document).ready(function() {
$("label[for='id_category1'], #id_category1, label[for='id_subcategory1'],#id_subcategory1 ").hide();
$('select[name=subcategory]').empty();
$('select[name=subcategory]').prepend('<option value="Not selected" selected disabled>Select Category...</option>');
$('select[name=subcategory1]').empty();
$('select[name=subcategory1]').prepend('<option value="Not selected" selected disabled>Select Category...</option>');
// called when category field changes from initial value
$('#id_group').change(function() {
var $this = $(this);
if ($this.find('option:selected').attr('value') == 'premium') {
$("label[for='id_category1'], #id_category1").show();
$("label[for='id_subcategory1'], #id_subcategory1").show();
} else {
$("label[for='id_category1'], #id_category1").hide();
$("label[for='id_subcategory1'], #id_subcategory1").hide();
}
})
$('#id_category').change(function() {
var $this = $(this);
if ($this.find('option:selected').index() !== 0) {
category_id = $('select[name=category]').val();
request_url = '/get_subcategory/' + category_id + '/';
$.ajax({
url: request_url,
type: "GET",
success: function(data) {
$('select[name=subcategory]').empty();
$.each(data, function(key, value) {
$('select[name=subcategory]').append('<option value="' + key + '">' + value + '</option>');
});
}
})
}
})
$('#id_category1').change(function() {
var $this = $(this);
if ($this.find('option:selected').index() !== 0) {
category_id = $('select[name=category1]').val();
request_url = '/get_subcategory/' + category_id + '/';
$.ajax({
url: request_url,
type: "GET",
success: function(data) {
$('select[name=subcategory1]').empty();
$.each(data, function(key, value) {
$('select[name=subcategory1]').append('<option value="' + key + '">' + value + '</option>');
});
}
})
}
})
$("label[for='id_keywords']").html("Keywords 0/100");
$('#id_keywords').keyup(function() {
var charsno = $(this).val().length;
$("label[for='id_keywords']").html("Keywords (" + charsno + "/100)");
});
$("label[for='id_description']").html("Description 0/250");
$('#id_description').keyup(function() {
var charsno = $(this).val().length;
$("label[for='id_description']").html("Description (" + charsno + "/2500)");
});
});
Thank you for any clues to beginner.
ALWAYS separate javascript from HTML.
ALWAYS separate and load separate javascript files according to separate functionality.
Then try and break down into MVC type architecture. I've been there where a javascript file is 2000 lines long - its unmanageable for editing and debugging.
ALWAYS try and use classes.
Class architecture is pseudo in Javascript but:
var view;
var model;
var ctrl;
/** - js_include_mvc.js
* as per:
* http://stackoverflow.com/questions/15192722/javascript-extending-class
* provides the bare minimum facility to instantiate MVC objects if the base mvc classes are not subclassed
* this ensures there is always js mvc
*
*/
Ctrl.inherits(BaseCtrl);
function Ctrl(args) {
BaseCtrl.apply(this, arguments);
Ctrl.prototype.boot = function () {
}
}
View.inherits(BaseView);
function View(args) {
BaseView.apply(this, arguments);
View.prototype.boot = function () {
}
}
Model.inherits(BaseModel);
function Model(args) {
BaseModel.apply(this, arguments);
Model.prototype.boot = function () {
}
}
Then for example:
function BaseView() {
BaseView.prototype.somemethod = function () {
//some functionality here
return "I am a View";
}
}
And finally call it in the global page script:
var view=new BaseView();
view.somemethod();
outputs:
"I am a view"
I term the class BaseView as I put code that is always reused here, however the BaseView class can be further extended according to application. So BaseView is used a a main repository for all my apps and I extend it according to requirements.
Some of my code comments need a rewrite but my Ajax class goes like this:
if (SERVER_INTERFACES == undefined) {
var SERVER_INTERFACES = {};//global server array required for named multiple servers and asynch data callbacks
}
/** Ajax_js - communicates with the server to retrieve data
*
* #param String newName is the name this class can reference for things like callbacks
* #param Strin newUrl
* #param String newNamedServer is the resource url defined as a php ajax server class
* #returns {Ajax}
*/
function Ajax_m(newName, newUrl, newNamedServer) {
this.namedServer = newNamedServer;
this.interface_name = newName;
this.url = newUrl;
this.server_data; //allows a query to be given an id and the result data stored and accessible without having to manipulate into method arguments which might require data changes
this.server_data = new Array(); //allows a query to be given an id and the result data stored and accessible without having to manipulate into method arguments which might require data changes
this.request_index = 0;
this.multiqueryindex = 0;
this.multiqueryctrl = new Array();
this.dataType = "json";
this.reqType = "post";
SERVER_INTERFACES[this.interface_name] = this;
Ajax_m.prototype.request = function (requestobj, callback, optargs) {
optargs = model.array_defaults(optargs, {runtagvals: {}});
if (optargs.id == undefined) {
this.request_index++;
} else {
this.request_index = optargs.id;
}
var request_id = this.request_index;
if (typeof requestobj == "string") {
//legacy was the requestobj as a string which is set to request and defaults assumed
//attempt to parse ajaxRequestSubType as first 'word'
var clauseend = requestobj.indexOf(" ");
var ajaxRequestSubType = requestobj.substr(0, clauseend);
requestobj = {ajaxRequestSubType: ajaxRequestSubType, request: requestobj, callback: callback, request_id: request_id};
} else {
//for legacy if callback and id are defined they are added but if in requestobj they will be overridden
var args = {callback: callback, request_id: request_id};
for (var r in requestobj) {
args[r] = requestobj[r];
}
requestobj = args;
}
//ensure default settings
var requestbuild = model.array_defaults(
requestobj,
{
request: null,
callback: "",
errorCallback: null,
request_id: null,
interface_name: this.interface_name,
responsedata: null,
multirequestid: null,
ajaxRequestSubType: "",
ajaxRequestType: "database", //default to use database cfg definitions of site
ismultiparent: false,
_method: "PATCH"//for laravel5
}
);
requestbuild.request = model.runtagreplace(requestbuild.request, optargs.runtagvals);
this.server_data[request_id] = requestbuild;
//debug in chrome as firebug fucks up badly with switch control
switch (requestbuild.ajaxRequestSubType) {
//control processes so no actual ajax requests are required
case "multirequest_parent":
case "multilrequest_submit":
break;
default:
this.server_data[request_id].ajax = $.ajax({
headers: {
'X-CSRF-TOKEN': laravelCSRF//this is a constant from php JSCONSTANTS
},
url: this.url,
type: this.reqType,
data: requestbuild,
dataType: this.dataType,
success: function (server_response) {
var requestobj = SERVER_INTERFACES[server_response.interface_name]; //.server_data[request_id];
if (requestobj.server_data[request_id] != undefined) {//check existence - a reset might have been requested since query sent
requestobj.setRequestResult(server_response.request_id, server_response.data);
//check through request types to eventual detect a simple callback from client object
switch (server_response.ajaxRequestSubType) {
case "select":
case "call":
if (server_response.flag_multiRequestChild) {
var parentinterface_name = requestobj.interface_name;
var parentinterface_id = server_response.multirequest_parent;
var multiobj =
SERVER_INTERFACES[parentinterface_name].
server_data[parentinterface_id];
multiobj.request_returned++;
if (multiobj.request_returned == multiobj.request_total) {
requestobj.multiRequestFinish(requestobj.server_data[request_id].multirequest_parent);
}
} else if (server_response.callback != "") {
eval(server_response.callback + "(" + server_response.request_id + ",'" + requestobj.interface_name + "')");
}
break;
case "submit":
if (!server_response.ismultipart) {
if (server_response.callback != "") {
eval(server_response.callback + "(" + server_response.request_id + ")");
}
} else {
var multiobj = SERVER_INTERFACES[server_response.interface_name].server_data[requestobj.server_data[request_id].multisubmit_parent];
multiobj.submit_returned++;
if (multiobj.submit_returned == multiobj.submit_total) {
requestobj.multiSubmitFinish(requestobj.server_data[request_id].multisubmit_parent);
}
}
break;
case "jscsv":
var downloadobj = SERVER_INTERFACES[server_response.interface_name].server_data[request_id];
requestobj.downloadrun(downloadobj);
break;
default://need failover as cannot determine what to do
break;
}
} else {
var faildata = "error";
}
},
error: function (server_response) {
//parse unhelpful error 'data' to relocate correct ajax request object
var errordata = this.data.split("&");
var intefacename;
var requestid;
var errorobj = {isajaxerror: true};
for (var i in errordata) {
var keyval = errordata[i].split("=");
errorobj[keyval[0]] = keyval[1];
if (errordata[i].indexOf("request_id=") != -1) {
requestid = errordata[i].substr(errordata[i].indexOf("=") + 1);
}
if (errordata[i].indexOf("interface_name=") != -1) {
interfacename = errordata[i].substr(errordata[i].indexOf("=") + 1);
}
}
var parentobj = SERVER_INTERFACES[interfacename];
var requestobj = parentobj.server_data[requestid];
//new object required as out of scope
errorobj["responseText"] = server_response.responseText;
parentobj.setRequestResult(errorobj["request_id"], errorobj);
eval(errorobj["callback"] + "(" + errorobj["request_id"] + ")");
}
});
break;
}
return request_id;
}
/*
* handles ajax where data is not expected back such as an insert statement or email send
* but can expect a response message such as 'ok' or an error message
*/
Ajax_m.prototype.submit = function (type, submitdata, callback) {
this.request({ajaxRequestSubType: "submit", type: type, submitdata: submitdata, ismultipart: false, callback: callback});
}
/*
* 'multiSubmit' handles ajax where data is not expected back such as an insert statement or email send
* but can expect a response message such as 'ok' or an error message
* EXAMPLE
var multisub = [
{
type: "update",
data: {
table: "[xondbs1].stats.dbo.a_ppuser",
values: {'email': 'tim'},
matchfield: 'keyval', matchvalue: 'Q00010017'
}
},
{
type: "update",
data: {
table: "[xondbs1].stats.dbo.a_ppuser",
values: {'email': 'tim'},
matchfield: 'keyval', matchvalue: 'Q00010017'
}
}
];
ajaxdbobj.multiSubmit(multisub, "callbackfunctionname");
*/
Ajax_m.prototype.multiSubmit = function (submitobject, callback) {
var parent_request_id = this.request({ajaxRequestSubType: "multisubmit_parent", submit_total: count(submitobject), submit_returned: 0, id_list: [], submit: {}, response: {}, callback: callback});
for (var s in submitobject) {
var child_request_id = this.request({
ajaxRequestSubType: "submit",
multisubmit_parent: parent_request_id,
ismultipart: true,
type: submitobject[s].type,
submitdata: submitobject[s].data
});
this.server_data[parent_request_id].id_list[child_request_id] = s;
}
return parent_request_id;
}
/*
* sets up mutli queries to only run callback when all complete
* the requestobject is key=>query assoc to which results are assign the same key
* when all results complete the callback is run giving the request_id in the normal way
* to return the multi result object
*/
Ajax_m.prototype.multiRequest = function (requestobject, callback) {
var parent_request_id = this.request({ajaxRequestSubType: "multirequest_parent", request_total: count(requestobject), request_returned: 0, id_list: [], request: {}, data: {}, callback: callback});
for (var r in requestobject) {
/*var child_request = {
request: requestobject[r],
ajaxRequestSubType: "multirequest_child",
}*/
var child_request = requestobject[r];
child_request.multirequest_parent = parent_request_id;
child_request.flag_multiRequestChild = true;
var child_request_id = this.request(child_request);
this.server_data[parent_request_id].id_list[child_request_id] = r;
}
return parent_request_id;
}
/*
* sets up facility for javascript to download data locally
* there is no callback facility required as this is a one-way request
* with a url returned pointing to the resultant file, this is ultimately sent as a new
* window request to the file which will be handled on the native machine settings
*
* for integrity and security the server sets a unique filename ending
*
* First a meta query is sent to process what action to take - eg inform that data is emailed when ready if a length query
* This is then fed back to the user as to what is happening before the actual query can begin.
* #param type determines what download process should be used
* #param dataArgs determines how the data is retrieved
*/
Ajax_m.prototype.requestDownload = function (fileprefix, type, downloadData) {
view.dialogOpen({
title: "Download",
dialogbody: "preparing download . . .",
closeButton: false//change to false after test
});
this.request({ajaxRequestType: "requestDownload", fileprefix: fileprefix, ajaxRequestSubType: type, downloadData: downloadData});
//this.server_data[downloadid].callbacktype = action;
}
/*
* opens url to processed data downloadRequest in a new window / tab
*/
Ajax_m.prototype.downloadrun = function (reqobj) {
//getRequestResult
var meta = this.getRequestResult(reqobj.request_id);
switch (reqobj.ajaxRequestSubType) {
case "jscsv":
view.dialogOpen({
title: "Download",
dialogbody: "Your file download has finished processing.<br />Please ensure you have popups enabled for this site to be able to access the file.",
closeOK: true
});
window.open(meta.downloadurl, "download");
break;
case "dbcsv":
view.dialogOpen({
title: "Download",
dialogbody: meta.msg,
closeOK: true
});
this.request({ajaxRequestSubType: "downloadrun", filename: reqobj.filename, type: reqobj.type, downloadsource: reqobj.downloadsource}, '');
break;
}
}
/*
* kills data (returning requested will be ignored)
*/
Ajax_m.prototype.reset = function () {
for (var s in this.server_data) {
if (this.server_data[s].ajax) {
this.server_data[s].ajax.abort();
}
}
this.server_data = new Array();
this.request_index = 0;
}
/*
* relates misc data to query eg
*/
Ajax_m.prototype.setMisc = function (request_id, miscobject) {
this.server_data[request_id].misc = miscobject;
}
/*
* gets misc data to query eg
*/
Ajax_m.prototype.getMisc = function (request_id) {
return this.server_data[request_id].misc;
}
/*
* get orig query sent
*/
Ajax_m.prototype.setAjaxRequestType = function (type) {
this.reqType = type;
}
/*
* get orig query sent
*/
Ajax_m.prototype.setDataType = function (type) {
this.dataType = type;
}
/*
* get orig query sent
*/
Ajax_m.prototype.getRequest = function (request_id) {
return this.server_data[request_id].request;
}
/*
* return result data for given request id
*/
Ajax_m.prototype.getRequestResult = function (id) {
var data;//leave as undefined so code fails in the client
if (this.server_data[id]) {
data = this.server_data[id].data;
}
return data;
//return data;
}
/*
* return result data for given request id indexed by a field name
* if its not unique there will be data loss and a 'distinct' set will be returned
*/
Ajax_m.prototype.getRequestResultByCol = function (id, colname) {
var reqdata = this.getRequestResult(id);
var data = {};
for (var r in reqdata) {
data.r[colname] = data.r;
delete data.r[colname][colname];
}
return data;
//return data;
}
/**
* return a single value for given request id, if request id did actual generate
* multiple values then only the first is returned
* if this was a multirequest, the named key of the multi request is required
*/
Ajax_m.prototype.getRequestValue = function (id, multi_id) {
var retval;
if (!this.server_data[id].ismultiparent) {
retval = this.server_data[id].data[0];
} else {
retval = this.server_data[id].data[multi_id][0];
}
if (retval == undefined) {
retval = null;
}
return retval;
}
/*
* removes a query and data from memory
*/
Ajax_m.prototype.deleteRequest = function (request_id) {
delete this.server_data[request_id];
}
Ajax_m.prototype.error = function (serverData, st, ert) {
//all errors should have been handled, but just in case
var errorReport = "error";
errorReport += st;
alert("error");
}
/**********************************************************************************************************
INTENDED AS PRIVATE FUNCTIONS
**********************************************************************************************************/
/*
* sets result data for this instance for given query id - eliminates the need for eval unknown ajax data
*/
Ajax_m.prototype.setRequestResult = function (request_id, data) {
this.server_data[request_id].data = data;
}
/*
* compiles the data from the multi query parts into the array entry referenced by the query_index returned
* from client code calling the multiquery function. This also allows the required callback with a reference id to this data
*/
Ajax_m.prototype.multiRequestFinish = function (multirequestid) {
var requestobj = this.server_data[multirequestid];
var multidata = {};
var compactdata;
for (var i in requestobj.id_list) {
requestobj.data[requestobj.id_list[i]] = this.getRequestResult(i);
this.deleteRequest(i);
}
eval(requestobj.callback + "(" + multirequestid + ")");
}
/*
* finalises multisubmit and runs callback
*/
Ajax_m.prototype.multiSubmitFinish = function (multirequestid) {
var requestobj = this.server_data[multirequestid];
var multidata = {};
var compactdata;
for (var i in requestobj.id_list) {
//requestobj.data[requestobj.id_list[i]] = this.getRequestResult(i);
this.deleteRequest(i);
}
eval(requestobj.callback + "(" + multirequestid + ")");
}
}
There were quite a few repetitions in your code. Contrary to #Datadimension I did not restructure your code completely but was trying to show you ways of parameterizing a few of your expressions and functions:
$(document).ready(function() {
var labcatsubcat1=
$("label[for='id_category1'],#id_category1,label[for='id_subcategory1'],#id_subcategory1 ")
.hide();
$('select[name=subcategory],select[name=subcategory1]')
.html('<option value="Not selected" selected disabled>Select Category...</option>');
// called when category field changes from initial value:
$('#id_group').change(function() {
labcatsubcat1.toggle($(this).val()=='premium');
})
$('#id_category,#id_category1').change(function() {
var $this = $(this), postfix=this.id.match(/1*$/)[0];
if ($this.find('option:selected').index() > 0) {
category_id = $('select[name=category'+postfix+']').val();
request_url = '/get_subcategory/' + category_id + '/';
$.ajax({
url: request_url,
type: "GET",
success: function(data) {
$('select[name=subcategory'+postfix+']').empty();
$.each(data, function(key, value) {
$('select[name=subcategory'+postfix+']').append('<option value="' + key + '">' + value + '</option>');
});
}
})
}
});
["keywords,Keywords,100","description,Description,250"].forEach(e=>{
var[id,nam,len]=e.split(",");
$("label[for='id_"+id+"']").html(nam+" 0/"+len);
$('#id_'+id).keyup(function() {
var charsno = $(this).val().length;
$("label[for='id_"+id+"']").html(nam+" (" + charsno + "/"+len+")");
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Unfortunately, this is not (yet) a working snippet, as you did not post your HTML ...
Just a short explanation regarding postfix: This variable will hold the string "1" or "" depending on, whether the this refers to the #id_category1 or the #id_category DOM-element.
One thing I'd do is combine the two AJAX requests. You can use multiple selectors for the change handler. Something like $('#id_category', '#id_category1').change(etc.... I'm sure you could find a way to target exactly which one is handling which request. You can use multiple selectors for the other functions as well as per the docs.
If the file is relatively short (~100 - 200 lines) I think you're fine keeping everything all in one place. Otherwise, definitely separate the functions into files and then require them as needed. You can use requireJS for this among others.
Related
I have Javascript function returning an object with an HTMLHtmlElement object I'd like to parse:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://someothersite.com/widgets/whatson.js"></script>
<script src="https://someothersite.com/widgets/jquery-deparam.js"></script>
<script>
var configuration = {
schedule: 'now'
};
if ( window.location.search ) configuration = $.extend({}, configuration, $.deparam( (window.location.search).replace('?','') ));
$(function(){
var content = $('div#output').whatsOn( configuration );
console.log( $("div#output").html() );
});
</script>
The above whatsOn function fills my output div with the HTML content returned, but the console log outputs nothing. If I write content to the console, it is an init object with data I'd like to parse. If I add the following, it sends an [object HTMLHtmlElement] to the console with all the data to the console:
var html = content.context.childNodes[1];
console.log( html );
How can I parse that the HTML? What I am after is the innerText element of the html variable above. However, if I change to content.context.childNodes[1].innerText, I get nothing.
UPDATED
Here is the code pasted:
/**
* Source for the Daily and Weekly Widgets
* Both of these widgets are rendered via the custom whatsOn Jquery plugin which is implemented via this script
*/
;
(function ($, window, document, undefined) {
/*
=== Begin async caching functions ===
Asynchronous Caching Code
The below code seems to be a direct copy of the code from https://learn.jquery.com/code-organization/deferreds/examples/
This code ensures that multiple requests are not made for loading the same data/resource.
Generic cache factory (below)
One promise is cached per URL.
If there is no promise for the given URL yet, then a deferred is created and the request is issued.
If it already exists, however, the callback is attached to the existing deferred.
*/
$.cacheFactory = function (request) {
var cache = {};
return function (key, callback) {
if (!cache[ key ]) {
cache[ key ] = $.Deferred(function (defer) {
request(defer, key);
}).promise();
}
return cache[ key ].done(callback);
};
};
// Every call to $.cacheFactory will create a cache respository and return a cache retrieval function
$.template = $.cacheFactory(function (defer, url) {
$.ajax({
url: url,
data: $.template.params,
dataType: 'jsonp',
success: defer.resolve,
error: defer.reject,
complete: defer.always
});
});
// === End async caching functions
// This function handles rendering template markup and data
$.renderTemplate = function (data, elem, options) {
var label = ( !options.label ) ? elem.addClass('whatson-hidden') : '';
return elem.empty().append(data) && label;
};
// Handles error message display
$.errorMessage = function (elem) {
return elem.append('error');
};
/*
=== Begin .whatsOn plugin functions ===
The following functions create a jquery plugin which can be executed on a JQuery element to render the whatson widget
The .whatsOn() functions are added to $.fn in order to create this plugin
- see https://learn.jquery.com/plugins/basic-plugin-creation/
*/
$.fn.whatsOn = function (options) {
// Do not initialize the widget if it has already been initialized, or if the station param is missing
if (this.data('whatson-init') || !options.station) {
return;
}
this.data('whatson-init', true); // widget-initialized flag
options = $.extend({}, $.fn.whatsOn.options, options);
// Force fix the old widget calls that do not use iframes.
if (options.env == 'http://composer.nprstations.org') options.env = 'https://api.composer.nprstations.org/v1';
$.fn.whatsOn.env = options.env;// Determines which API request URL to use
// Add CSS stylesheet
var css = document.createElement('link');
css.setAttribute('rel', 'stylesheet');
css.setAttribute('type', 'text/css');
css.setAttribute('href', options.assets + '/widgets/css/v2.css');
document.getElementsByTagName('head')[0].appendChild(css);
/*
Return the plugin object
When you filter elements with a selector ($('.myclass')), it can match more than only one element.
With each, you iterate over all matched elements and your code is applied to all of them.
*/
return this.each( function () {// BEGIN Return the plugin.
var elem = options.elem = $(this);
var ucs = options.station;
var schedule = options.schedule;
var times = options.times;
var next = options.next;
var show_song = options.show_song || null;
var display_style = options.style || null;
var request_url;
// The buy links
var hide_itunes = options.hide_itunes || null;
var hide_amazon = options.hide_amazon || null;
var hide_arkiv = options.hide_arkiv || null;
if (!options.affiliates) options.affiliates = {};
if (options.itunes) options.affiliates.itunes = options.itunes;
if (options.amazon) options.affiliates.amazon = options.amazon;
if (options.arkiv) options.affiliates.arkiv = options.arkiv;
elem.addClass('whatson-wrap');// Add widget class
$("body").attr('id', 'nprds_widget'); // Add an ID to the body of the document
// Ensure that the moment() JS plugin is available, if not, then add and load it (http://momentjs.com/docs/)
if (!window.moment) {
$.getScript('https://composer.nprstations.org/widgets/js/moment.isocalendar.js').done(function (script) {
builder();// Build plugin function (see below)
});
} else {
builder();// Build plugin function (see below)
}
// This function builds the plugin, it does all of the setup required
function builder() {// BEGIN build plugin function
// Set the date/range for the widget header. If its the daily widget use a date, for the weekly widget use a range
var req_date = ( options.schedule === 'week' )
? moment().isoday(1).format('YYYY-MM-DD') + ',' + moment().isoday(7).format('YYYY-MM-DD')
: moment().format('YYYY-MM-DD');
request_url = $.fn.whatsOn.env + '/widget/' + ucs + '/' + schedule;
$.template.params = {
format: 'jsonp',
date: req_date,
times: times,
show_song: show_song || null,
style: display_style || null,
hide_itunes: hide_itunes || null,
hide_amazon: hide_amazon || null,
hide_arkiv: hide_arkiv || null,
itunes: options.affiliates.itunes || null,
amazon: options.affiliates.amazon || null,
arkiv: options.affiliates.arkive || null
};
// Pull out empty fields
if (!$.template.params.itunes) delete $.template.params.itunes;
if (!$.template.params.amazon) delete $.template.params.amazon;
if (!$.template.params.arkiv) delete $.template.params.arkiv;
if (!$.template.params.show_song) delete $.template.params.show_song;
if (!$.template.params.style) delete $.template.params.style;
if (!$.template.params.hide_itunes) delete $.template.params.hide_itunes;
if (!$.template.params.hide_amazon) delete $.template.params.hide_amazon;
if (!$.template.params.hide_arkiv) delete $.template.params.hide_arkiv;
// force now playing to an uncached request
if (schedule === 'now') request_url = request_url + '?bust=' + ~~(Math.random() * 1E9);
/*
$.template(request_url) = Request widget data (from cache or a new request), uses cacheFactory (see code at the top of this script)
When the data has been received, handle the construction and rendering of the HTML
*/
$.when( $.template(request_url) ).done( function (data) {
$.renderTemplate(data, elem, options);
// Set up the widget header
if (schedule === 'week') {
var week_of = moment().isoday(1).format('[Week of] MMMM Do, YYYY');
$.fn.whatsOn.header(elem, week_of, options);
}
if (schedule === 'day') {
var day_date = moment().format('dddd, MMMM Do, YYYY');
$.fn.whatsOn.header(elem, day_date, options);
}
// Add a handler for when an Itunes link is clicked
$('.itunes-link').on('click', function (e) { // BEGIN itunes link click handler
e.preventDefault();// If this method is called, the default action of the event will not be triggered.
var searchUrl = $(this).attr('href').split('?');
var forwardTo = '';
var searchPairs = searchUrl[1].split('&');
var searchTerms = '';
var affiliate = '';
$.each(searchPairs, function (key, value) {
var pairs = value.split('=');
if (pairs[0] == 'at') {
affiliate = pairs[1];
}
else {
searchTerms = searchTerms + ' ' + pairs[1];
}
});
$.ajax({
url: 'https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/wsSearch?entity=musicTrack&country=US&term=' + searchTerms + '&at' + affiliate + '&limit=1&callback=?',
type: 'GET',
dataType: 'jsonp',
success: function (data) {
console.log('data', data)
if (data.resultCount > 0) {
if (data.results[0].collectionViewUrl) {
forwardTo = data.results[0].collectionViewUrl;
}
else {
forwardTo = data.results[0].artistViewUrl;
}
}
else {
forwardTo = 'https://itunes.apple.com/us/';
}
window.open(forwardTo, 'itunes');
},
error: function (err) {
console.log('err', err);
console.log(err);
window.open('https://itunes.apple.com/us/', 'itunes');
}
});
});// END itunes link click handler
$.fn.whatsOn.collapse();
}).always(function () {
if (schedule === 'now') {
setTimeout(builder, 60000);
}
}).fail(function () {
$.errorMessage(elem);
});
}// END build plugin function
});// END return the plugin
};
// Expand/collapse episode detail - This widgets allows for you to expand an episode to view it's playlist and episode notes.
$.fn.whatsOn.collapse = function () {
$('.playlist-controls').on('click.whatsOn', function () {
$(this).toggleClass('opener');
$(this).parent().find('.dailyPlaylist').toggle();
});
$('.notes-controls').on('click.whatsOn', function () {
$(this).toggleClass('opener');
$(this).parent().find('.episodeNotes').toggle();
});
};
// Construct header and setup events handler for the next/previous link
$.fn.whatsOn.header = function (elem, date, options) {
var nav_control_prev = '<td class="whatson-prev arrow-left"><a class="hidden" href="#">Previous</a></td>';
var nav_control_next = '<td class="whatson-next arrow-right"><a class="hidden" href="#">Next</a></td>';
var header_nav = [
'<table id="whatson-header" width="100%" border="0" cellspacing="0" cellpadding="0">',
'<tr>' + nav_control_prev + '<td class="whatson-date">' + date + '</td>' + nav_control_next + '</tr>',
'</table>'
].join('');
elem.prepend(header_nav);
// Events handler for navigational next/previous links
$(elem).find('.whatson-prev, .whatson-next').on('click', options, $.fn.whatsOn.nav);
};
// This is the event handler for the widget's next/previous links
$.fn.whatsOn.nav = function (e) {// BEGIN next/previous date/date-range handler
e.preventDefault();
var widget = e.data.elem;
var header = '.whatson-header';
var date_header = '.whatson-date';
var content = '.whatson-content';
var direction = ( $(e.currentTarget).is('.whatson-next') ) ? 'next' : 'prev';
var current_date = widget.find(date_header).text();
var moment_date = moment(current_date, 'dddd, MMMM D YYYY');
var station = e.data.station;
var schedule = e.data.schedule;
var affiliates = e.data.affiliates;
var full_week_date = current_date.replace('Week of ', '');
var request_url;
var options = {}; // Configuration
// Configure buy links for songs within episodes
if (affiliates.itunes) options.itunes = affiliates.itunes;
if (affiliates.amazon) options.amazon = affiliates.amazon;
if (affiliates.arkiv) options.arkiv = affiliates.arkiv;
if (e.data.hide_itunes) options.hide_itunes = e.data.hide_itunes;
if (e.data.hide_amazon) options.hide_amazon = e.data.hide_amazon;
if (e.data.hide_arkiv) options.hide_arkiv = e.data.hide_arkiv;
if (e.data.show_song) options.show_song = e.data.show_song;// Display song meta data
if (e.data.style) options.style = e.data.style;// Which template is being used
// This function updates the display after a new request is made
var updater = function (request_url, update_header) {
$('.opener, .closer').off('.whatsOn'); // Disable event handlers
$.when( $.template(request_url) ).done( function (data) { // After the request, update the display
widget.find(header)
.remove().end()
.find(date_header).html(update_header).end()
.find(content).replaceWith(data);
$.fn.whatsOn.collapse();
});
};
if (direction === 'next') { // The 'next' link was clicked
var next_from_current = moment(full_week_date, 'MMMM D YYYY').add('d', 7);
// Set the next date/date-range. If its the daily widget use a date, for the weekly widget use a range
var next_api = ( schedule === 'week' )
? '?date=' + next_from_current.isoday(1).format('YYYY-MM-DD')
+ ',' + next_from_current.isoday(7).format('YYYY-MM-DD')
: '?date=' + moment_date.add('d', 1).format('YYYY-MM-DD');
var next_header = ( schedule === 'week' )
? moment(next_from_current, 'MMMM D YYYY').isoday(1).format('[Week of] MMMM Do, YYYY')
: moment_date.format('dddd, MMMM Do, YYYY');
request_url = $.fn.whatsOn.env + '/widget/' + station + '/' + schedule + next_api;
$.template.params = $.extend({ format: 'jsonp' }, options);
updater(request_url, next_header);// Go ahead and update the display to be the 'next' date/date-range
} else {
// Set the previous date/date-range. If its the daily widget use a date, for the weekly widget use a range
var prev_from_current = moment(full_week_date, 'MMMM D YYYY').subtract('d', 7);
var prev_api = ( schedule === 'week' )
? '?date=' + prev_from_current.isoday(1).format('YYYY-MM-DD')
+ ',' + prev_from_current.isoday(7).format('YYYY-MM-DD')
: '?date=' + moment_date.subtract('d', 1).format('YYYY-MM-DD');
var prev_header = ( schedule === 'week' )
? moment(prev_from_current, 'MMMM D YYYY').isoday(1).format('[Week of] MMMM Do, YYYY')
: moment_date.format('dddd, MMMM Do, YYYY');
$.template.params = $.extend({ format: 'jsonp' }, options);
request_url = $.fn.whatsOn.env + '/widget/' + station + '/' + schedule + prev_api;
updater(request_url, prev_header);// Go ahead and update the display to be the 'previous' date/date-range
}
};// END next/previous date/date-range handler
// Default widget options
$.fn.whatsOn.options = {
schedule: 'now',
label: true,
env: 'https://api.composer.nprstations.org/v1',
assets: 'https://composer.nprstations.org',
affiliate: {},
times: true,
next: true
};
// === End .whatsOn plugin functions ===
})(jQuery, window, document);
I am try to call a function onblur of my three elements in my table
but i am getting this error in my console
"((m.event.special[e.origType] || (intermediate value)).handle || e.handler).apply is not a function".
So onblur these elements i want to call my function.Please do have a look at my code once.
Apologies for my bad handwriting here .I am new to javascript
jq.ajax({
url: '/soap/ajax/36.0/connection.js',
dataType: 'script',
success: function() {
jq(document).ready(function() {
// call tookit dependent logic here
jq('#gbMainTable').on('blur', 'tr.dr td[name="v4"] select','tr.dr td[name="v6"] select','tr.dr td[name="v7"] input', function()
{
var thisInput = this;
console.log('thisInput.value',thisInput.value);
var thisInput1 = jq(this);
var thisRow = thisInput1.parents('tr.dr:first');
console.log('thisRow',thisRow);
var agentInput1 = thisRow.find('td:eq(7)');
console.log('agentInput1',agentInput1);
var finds1 = agentInput1.find('select');
console.log('finds1',finds1);
var agentInput2 = thisRow.find('td:eq(8)');
console.log('agentInput2',agentInput2);
var finds2= agentInput2.find('input');
if(agentInput1.text()!=='' & thisInput.value=='Programmable Cntrls/Welders/AC Drives/Soft Starts'){
exampleFunction('Programmable',agentInput1.text(),agentInput2.text());
console.log('YES');
}
else if(agentInput1.text()!=='' & thisInput.value=='Electro Mechanical'){
exampleFunction('Electro-Mechanical',agentInput1.text(),agentInput2.text());
}
});
});
},
async: false
});
function exampleFunction(Warranty,Years,charge) {
var state = { // state that you need when the callback is called
output : null,
startTime : new Date().getTime()};
var callback = {
//call layoutResult if the request is successful
onSuccess: doThis,
//call queryFailed if the api request fails
onFailure: queryFailed,
source: state};
sforce.connection.query(
"SELECT InOut__c,PlantPct__c,T37Pct__c,TotalPct__c,Type__c,Warranty_Code__c,Years__c FROM WarrantySpecialLine__c where InOut__c = '" +charge+"' and Years__c = '"+Years+"' and Type__c = '" +Warranty +"'",
callback);
}
function queryFailed(error, source) {
console.log('error: '+error);
}
/**
* This method will be called when the toolkit receives a successful
* response from the server.
* #queryResult - result that server returned
* #source - state passed into the query method call.
*/
function doThis(queryResult, source) {
if (queryResult.size > 0) {
var output = "";
// get the records array
var records = queryResult.getArray('records');
// loop through the records
for (var i = 0; i < records.length; i++) {
var warrantySpecial = records[i];
console.log(warrantySpecial.PlantPct__c + " " + warrantySpecial.TotalPct__c + " " +warrantySpecial.T37Pct__c );
}
}
}
I have a search form to call a Solr index, filled with geolocations:
jQuery('.form-submit', element).click(function (e) {
e.preventDefault();
search();
});
function search() {
useBBOX = false;
combinedExtent = ol.extent.createEmpty();
data.map.getLayers().forEach(function (layer, index, array) {
if (layer.getSource() instanceof ol.source.Vector) {
var source = layer.getSource().getSource();
var url = data.opt.urls[source.get('machineName')];
var newSource = new ol.source.Vector({
loader: getLoader(data, url),
format: new ol.format.GeoJSON(),
strategy: ol.loadingstrategy.bbox,
reloadOnZoomChange: true,
reloadOnExtentChange: true
});
newSource.set('machineName', source.get('machineName'));
var newCluster = new ol.source.Cluster({
source: newSource,
distance: 200
});
layer.setSource(newCluster);
}
});
}
function getLoader(data, url) {
return function (extent, resolution, projection) {
var bbox = ol.proj.transformExtent(extent, data.map.getView().getProjection(), 'EPSG:4326');
var params = {};
if (data.opt.paramForwarding) {
var get_params = location.search.substring(location.search.indexOf('?') + 1).split('&');
jQuery.each(get_params, function (i, val) {
if (val.length) {
var param = val.split('=');
params[decodeURIComponent(param[0])] = (param[1] !== undefined) ? decodeURIComponent(param[1].replace(/\+/g, ' ')) : '';
}
})
}
if (useBBOX == true) {
params.bbox = bbox.join(',');
params.zoom = data.map.getView().getZoom();
}
var searchQuery = jQuery('#input-search-address').val();
if (searchQuery != 'undefined' && searchQuery != null) {
url = url.substr(0, url.lastIndexOf("/") + 1);
url = url + searchQuery;
}
jQuery(document).trigger('openlayers.bbox_pre_loading', [{
'url': url,
'params': params,
'data': data
}]);
var that = this;
jQuery.ajax({
url: url,
data: params,
success: function (responsdata) {
var features = that.getFeaturesInExtent(extent);
jQuery(features).each(function (i, f) {
that.removeFeature(f);
});
var format = new ol.format.GeoJSON();
var features = format.readFeatures(responsdata, {featureProjection: projection});
that.addFeatures(features);
that._loadingFeatures = false;
if (!ol.extent.isEmpty(that.getExtent())) {
combinedExtent = ol.extent.extend(combinedExtent, that.getExtent());
if (useBBOX == false) {
useBBOX = true;
}
}
}
});
};
}
Basically, it fetches 3 layers, each containing a number of markers. I'ld like to autozoom the map based on those markers. Therefore I'm looking for the extent. The combinedExtent does contain all correct extents...
... but when I add data.map.getView().fit(combinedExtent, data.map.getSize()) INSIDE the getLoader function, it's not working. I looks like only 1 extent get plotted on the map.
Whenever I try to log the combinedExtent in the search() function, I get a weird error...
Google told me I had to wait until the getState() of newSource was ready, but that didn't work out...
So, I'm looking for a solution. My guess would be the use of the ajax return in getLoader...
I just had this issue, so I have a function to listen until the source is ready and finished loading before giving a count of features (otherwise it would update the log with every iteration). Just change my source name (any variable with "parcelquery in it) with yours and hopefully it will at least put you in the right direction.
var listenerKey = wfsSource_parcelquery.on('change', function(e) {
if (wfsSource_parcelquery.getState() == 'ready') { //says source is done loading
var featureCount = wfsSource_parcelquery.getFeatures().length; //getting number of features resulting from query
ol.Observable.unByKey(listenerKey);
// use vectorSource.unByKey(listenerKey) instead
// if you do use the "master" branch of ol3
}
alert("Your query returned "+ featureCount + " results.");
var extent = lyr_parcelquery.getSource().getExtent();
console.log(extent);
map.getView().fit(extent, map.getSize());
});
I'm trying to make a REST connection to Amazon S3 and I'm struggling to implement the signature V4 process. I know, Amazon also provides a JavasScript an sdk, but I need to get it to work by myself.
To test my code, I'm using the AWS testsuite and my code successfully returns the expected results. However, all my real life attempts fail because of signature does not match error.
The issue for me is, the examples don't include any headers except host and date which is kind of straightforward. I'm having to pass the "full monty" including access tokens and I'm sure there is a problem with the headers.
Here is what I'm doing on click of a button:
function simpleS3Scenario(my_storage) {
return new RSVP.Queue()
.push(function () {
return my_storage.allDocs();
})
.push(function (my_alldocs_response) {
console.log("initial ALLDOCS")
console.log(my_alldocs_response);
})
...
};
allDocs will eventually run into this:
S3Storage.prototype.buildQuery = function (param, options) {
var context = this,
url = this._base_url,
url_path = this._bucket + "/",
headers,
requestText;
return new RSVP.Queue()
.push(function () {
return SigV4_custom.makeSignedRequest(
"GET",
url + url_path,
'',
{"max-key": 100, "prefix": "sample_folder"},
{},
context._region,
"s3",
"AWS4-HMAC-SHA256",
context._secret_access_key,
context._access_key,
context._session_token
);
})
.push(function (e) {
console.log("YUPI");
console.log(e);
})
...
which enters my request signature handler, which is based on this (not working with Signature v4) Amazon example | code: sigv4.js:
(sorry, not shortened yet, scroll down to ENTRY POINT, then work yourself up)
var SigV4_custom = new function(){
this.createCanonicalRequest = function(){};
this.createStringToSign = function(){};
this.generateSignatureAndSign = function(){};
this.makeSignedRequest = function(){};
}
// trim polyfill
String.prototype.trim = String.prototype.trim || function () {
var start = -1,
end = this.length;
while( this.charCodeAt(--end) < 33 );
while( this.charCodeAt(++start) < 33 );
return this.slice( start, end + 1 );
};
// =============== generator funtions =================
// generate query parameter string for canonical request
function generateCanonicalQueryString(my_parameter_dict) {
var canonical_query_string = '',
encoded_parameter_dict = {},
key_list = [],
key,
value,
i,
i_len;
// set and encode
for (key in my_parameter_dict) {
if (my_parameter_dict.hasOwnProperty(key)) {
value = my_parameter_dict[key];
if (typeof value === "object") {
encoded_parameter_dict[encodeURIComponent(key)] =
encodeURIComponent(value[0]) + '&';
// Append each additional value to the query parameter
for (i = 0, i_len = value.length; i < i_len; i += 1) {
encoded_parameter_dict[encodeURIComponent(key)] +=
encodeURIComponent(key) + '=' + encodeURIComponent(value[i]) +'&';
}
encoded_parameter_dict[encodeURIComponent(key)] =
encoded_parameter_dict[encodeURIComponent(key)].slice(0,-1);
} else {
encoded_parameter_dict[encodeURIComponent(key)] =
encodeURIComponent(value);
}
}
}
// then fill key_list
for (key in encoded_parameter_dict) {
if (encoded_parameter_dict.hasOwnProperty(key)) {
key_list.push(key);
}
}
key_list.sort();
for (i = 0, i_len = key_list.length; i < i_len; i += 1) {
canonical_query_string +=
key_list[i] + "=" + encoded_parameter_dict[key_list[i]] + "&";
}
return canonical_query_string.slice(0, -1);
}
// generate canonical header string
function generateCanonicalHeaderString(my_header_dict, is_signature) {
var canonical_header_string = '',
encoded_header_dict = {},
header_list = [],
header,
value,
trimmed_value,
header_lowercase,
i,
i_len,
separator,
connector,
format,
cutoff;
if (is_signature) {
cutoff = -1;
separator = ":";
connector = "\n";
format = function (my_value) {
return my_value.toLowerCase();
}
} else {
cutoff = -1;
separator = "=";
connector = "&";
format = function (my_value) {
return my_value;
}
}
// Take header keys and put in array, then sort and build
for (header in my_header_dict) {
if (my_header_dict.hasOwnProperty(header)) {
header_lowercase = format(header);
value = my_header_dict[header];
trimmed_value = undefined;
//XXX there are not only strings in the headers...
if (value.trim) {
trimmed_value = value.trim();
}
if (encoded_header_dict[header_lowercase] === undefined) {
encoded_header_dict[header_lowercase] = trimmed_value || value;
header_list.push(header_lowercase);
} else {
encoded_header_dict[header_lowercase] += "," + trimmed_value || value;
}
}
}
header_list.sort();
for (i = 0, i_len = header_list.length; i < i_len; i += 1) {
canonical_header_string +=
header_list[i] +
separator +
encoded_header_dict[header_list[i]] +
connector;
}
canonical_header_string = canonical_header_string.slice(0, cutoff);
return canonical_header_string;
}
// generate the signed header string
function generateSignedHeaderString(my_header_dict) {
var signed_header_string = '',
header_list = [],
header,
header_lowercase,
i,
i_len;
for (header in my_header_dict) {
if (my_header_dict.hasOwnProperty(header)) {
header_list.push(header.toLowerCase());
}
}
header_list.sort();
for (i = 0, i_len = header_list.length; i < i_len; i += 1) {
signed_header_string += header_list[i] + ';';
}
return signed_header_string.slice(0, -1);
}
// returns a timestamp in the YYYYMMDD'T'HHmmSS'Z' format, for SigV4 calls
function generateTimestamp() {
var date = new Date();
function bump(my_time_parameter) {
if (my_time_parameter.length === 1) {
return '0' + my_time_parameter;
}
return my_time_parameter;
}
// Assemble date, bump single digits to doubles
// validation: return "20130524T000000Z";
return date.getUTCFullYear() +
bump((date.getUTCMonth()+1).toString()) + // month
bump(date.getUTCDate().toString()) + // day
'T' +
bump(date.getUTCHours().toString()) + // hour
bump(date.getUTCMinutes().toString()) + // minute
bump(date.getUTCSeconds().toString()) + // second
'Z';
}
// generate the credential scope
function generateCredentialScope(my_timestamp, my_region, my_service) {
return my_timestamp.substring(0, 8) + "/" +
my_region + "/" +
my_service + "/" +
"aws4_request";
}
// ================== CORE methods ==================
SigV4_custom.createStringToSign = function (request_time, credential_scope,
canonical_request, signing_algorithm) {
// Step 1: designate the algorithm (for SigV4 it is SHA256)
// Step 2: designate the RequestDate (already done, passed to function)
// Step 3: create credentialScope (already done, passed to funtion)
return signing_algorithm + '\n' +
request_time + '\n' +
credential_scope + '\n' +
canonical_request;
}
SigV4_custom.generateSignatureAndSign = function(secret, request_time, region,
service, string_to_sign) {
var datestamp = request_time.substring(0, 8),
hash_date,
hash_region,
hash_service,
hash_signing,
request = 'aws4_request';
hash_date = CryptoJS.HmacSHA256(datestamp, 'AWS4' + secret, {asBytes: true});
hash_region = CryptoJS.HmacSHA256(region, hash_date, {asBytes: true});
hash_service = CryptoJS.HmacSHA256(service, hash_region, {asBytes: true});
hash_signing = CryptoJS.HmacSHA256(request, hash_service, {asBytes: true});
// sign and return
return CryptoJS.HmacSHA256(string_to_sign, hash_signing)
.toString(CryptoJS.enc.Hex);
}
SigV4_custom.createCanonicalRequest = function(method, pathname,
parameter_dict, header_dict, hashed_payload) {
var canonical_request = "",
http_request_method = "",
canonical_url = "",
canonical_query_string = "",
canonical_header_string = "",
signed_header_string = "",
payload;
// Step 1: Start with the HTTP request method
http_request_method = method;
// Step 2: Add the canonicalUrl parameter
canonical_url = pathname;
// Step 3: Add the canonicalQueryString parameter
canonical_query_string = generateCanonicalQueryString(parameter_dict);
// Step 4: Add the canonicalHeaders parameter
canonical_header_string = generateCanonicalHeaderString(header_dict, true);
// Step 5: Add the signedHeaders parameter
signed_header_string = generateSignedHeaderString(header_dict);
// Step 6: Add the hashed payload
payload = hashed_payload;
// Step 7: string together the canonicalRequest from steps 1 through 6
canonical_request += http_request_method + '\n' +
canonical_url + '\n' +
canonical_query_string + '\n' +
canonical_header_string + '\n\n' +
signed_header_string + '\n' +
payload;
return canonical_request;
};
// =================== ENTRY POINT ===================
// Call this function to make a SigV4 REST API call.
SigV4_custom.makeSignedRequest = function (method, path, payload,
parameter_dict, header_dict, region, service, signing_algorithm,
secret_access_key, access_key_id, security_token) {
var link = document.createElement("a"),
request_time = generateTimestamp(),
scope = generateCredentialScope(request_time, region, service),
authorization_string,
canonical_request,
string_to_sign,
request_uri,
signature;
// link is a location object, set its href to use its properties
link.href = path;
// set all headers, then generate a canonical_request
// payload must be hashed here, as this may vary and the hash is required
// for canonization and as a header. Empty payloads will hash an empty "".
header_dict['X-Amz-Content-Sha256'] = CryptoJS.SHA256(payload)
.toString(CryptoJS.enc.Hex);
header_dict['host'] = link.hostname;
header_dict['X-Amz-Date'] = request_time;
header_dict['X-Amz-Credential'] = access_key_id + "/" + scope;
header_dict['X-Amz-Security-Token'] = security_token;
header_dict['X-Amz-Algorithm'] = signing_algorithm;
header_dict['X-Amz-Expires'] = 86400;
// Task 1: Compute the canonical request
canonical_request = CryptoJS.SHA256(
this.createCanonicalRequest(
method,
link.pathname,
parameter_dict,
header_dict,
header_dict['X-Amz-Content-Sha256']
)
)
.toString(CryptoJS.enc.Hex);
// delete host because it is also in the headers (needs to be in both
// initially to create the canonicalization)
delete parameter_dict['host'];
// Task 2: Create the string to sign using the encoded canonical request
string_to_sign = this.createStringToSign(
request_time,
scope,
canonical_request,
signing_algorithm
);
// Task 3: Create the signature using the signing key and the string to sign
signature = this.generateSignatureAndSign(
secret_access_key,
request_time,
region,
service,
string_to_sign
);
// Task 4: Build the authorization header
header_dict['Authorization'] = header_dict['X-Amz-Algorithm'] +
" Credential=" + header_dict['X-Amz-Credential'] + "," +
"SignedHeaders=" + generateSignedHeaderString(header_dict) + "," +
"Signature=" + signature;
// Task 5: Make the request (through jIO)
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
"method": method,
"url": path,
"headers": header_dict
});
});
}
So when I run the Amazon samples with just two headers through this, I get correct signature and authorization header at the end. I believe I'm doing something wrong with the headers, especially which headers should go into the signature, but I can't find any good help online and am stuck on this for a few days now.
Question:
Maybe someone has an idea from looking at how I'm handling the headers, why my requests to AWS fail due to non-matching signature?
THANKS!
Edit:
I have used this Amazon Example page for deriving signatures. If I run the sample inputs through my createSignatureAndSign method, I can produce the same outputs. So my signature is correct an secret are correct.
I have the following javascript code having class named as PurchaseHistory.
var baseUrl = null;
var parameters = null;
var currentPageNumber = null;
var TotalPages = null;
var PageSize = null;
$(document).ready(function () {
baseUrl = "http://localhost/API/service.svc/GetOrderHistory";
parameters = '{"userId":1 , "page":1 ,"pageSize":4}';
currentPageNumber = 1;
var history = new PurchaseHistory();
history.ajaxCall(parameters);
});
function PurchaseHistory() {
/* On ajax error show error message
-------------------------------------------------*/
this.onAjaxError = function() {
$('#error').text('No internet connection.').css('color', 'red');
}
/* Ajax call
-------------------------------------------------*/
this.ajaxCall = function (parameters) {
$.support.core = true;
$.ajax({
type: "POST",
url: baseUrl,
data: parameters,
//dataType: 'json',
contentType: "application/json; charset=UTF-8",
error: function () { this.onAjaxError() }
}).done(function (data) {
var json = data.GetOrderHistoryResult;
var jsonObject = $.parseJSON(json);
var history = new PurchaseHistory();
history.populateOrderHistory(jsonObject);
TotalPages = jsonObject.PgCnt;
currentPageNumber = jsonObject.CrntPg;
});
}
this.populateOrderHistory = function(results) {
var rowOutput = "";
var his = new PurchaseHistory();
for (var i = 0; i < results.Results.length; i++) {
rowOutput += this.renderCartList(results.Results[i], 1);
}
}
this.renderCartList = function(res, i) {
var container = $('#prototype-listelement>li').clone();
container.find('.order-date').text(res.OdrDate);
container.find('.item-count').text(res.Qty);
container.find('.total').text(res.Amt);
container.find('.order-id').text(res.OdrId);
$('#mainul').append(container).listview('refresh');
}
this.loadNextPage = function () {
parameters = '{"userId":1 , "page":' + currentPageNumber + 1 + ',"pageSize":4}';
this.ajaxCall(parameters);
}
}
The ajaxCall is made on the ready function of the javascript.
This ajax calls returns Json object with pages information, which includes current page number, total pages and page size.
Currently I am able to display the information on the UI, when the page gets loaded.
My Issue:-
I want to call the ajax method again, on the button click event.
How this can be made possible and where can I store the information obtained from previous ajax call?
For pagination I would create a link that will load more items onto the page, and save a starting number to pass to your data layer. This example loads 20 at a time.
<a class="more" href="#" data-start="0">show more</a>
$("a.more").click(function(e){
e.preventDefault();
var start = $(this).attr('data-start');
$.get('/more-data, { start: start }, function(d){
var next = start+20;
$("a.more").attr('data-start', next);
//process results here, do something with 'd'
});
});