Javascript sanity check of netsuite suitescript - javascript

Currently, I have a Netsuite SuiteScript where I export saved searches to csv's. It runs but it's not reusable and I'd like to make it easier by just adding key-value pairs. I have to a lot of copy pasting and it's easy to forget to update to the latest iteration of the run. It's a scheduled search which means it runs every 15 minutes and makes it hard to debug and test.
Right now, my code looks like this, but with more searches, and it's a pain is not reusable.
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(task) {
var FILE_ID = 2992639;
var SEARCH_ID = 2993;
function execute(scriptContext) {
//first search
var searchTask1 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask1.savedSearchId = SEARCH_ID;
searchTask1.fileId = FILE_ID;
var searchTaskId1 = searchTask1.submit();
//next search
FILE_ID = 2992640;
SEARCH_ID = 3326;
var searchTask2 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask2.savedSearchId = SEARCH_ID;
searchTask2.fileId = FILE_ID;
var searchTaskId2 = searchTask2.submit();
//next search
FILE_ID = 2992634;
SEARCH_ID = 3327;
var searchTask3 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask3.savedSearchId = SEARCH_ID;
searchTask3.fileId = FILE_ID;
var searchTaskId3 = searchTask3.submit();
//this pattern repeats 19 times total.
}
return {
execute: execute
};
});
I've tried to code below
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(task) {
const searchList = {
2993:2992639,
3326:2992640,
3327:2992634
};
function execute(scriptContext) {
for (const [key, value] of Object.entries(searchList)) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = $key;
searchTask.fileId = $value;
var searchTaskId = searchTask.submit();
}
}
return {
execute: execute
};
});
but get the following error, and I'm not sure what is wrong with my syntax. Netsuite makes it hard to tell what I'm doing wrong, so I'm hoping someone can help here. Thanks!
{"type":"error.SuiteScriptError","name":"SSS_MISSING_REQD_ARGUMENT","message":"task.submit: Missing a required argument: SearchTask.savedSearchId","stack":["createError(N/error)","execute(/SuiteScripts/dashboardreports.js:224)","createError(N/error)"],"cause":{"name":"SSS_MISSING_REQD_ARGUMENT","message":"task.submit: Missing a required argument: SearchTask.savedSearchId"},"id":"","notifyOff":false,"userFacing":true}

I would use a custom record to store the search and file IDs, so you can add/update without modifying code. Then I would do something like the code below. This code first queries your custom record to get all of the search and file ids, then for each one, starts a new task.
/**
* #NApiVersion 2.0
* #NScriptType ScheduledScript
* #NModuleScope Public
*/
define(['N/log', 'N/search', 'N/task'], function(log, search, task) {
function execute(context) {
var searchInfos = getSearchInfo();
searchInfos.forEach(function(searchInfo) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = searchInfo.searchId;
searchTask.fileId = searchInfo.fileId;
var searchTaskId = searchTask.submit();
log.debug({ title: 'searchTaskId', details: searchTaskId });
});
}
function getSearchInfo() {
var results = search.create({
type: 'customrecord_search_to_csv_info',
filters: [
['isinactive', 'is', 'F']
],
columns: [
'custcolumn_search_id',
'custcolumn_file_id'
]
}).run().getRange({ start: 0, end: 1000 });
return (results || []).map(function(result) {
return {
searchId: result.getValue({ name: 'custcolumn_search_id '}),
fileId: result.getValue({ name: 'custcolumn_file_id' })
}
});
}
return {
execute: execute
};
});

Related

Remove Specific Item From Array With Class Objects Inside

My Problem
I am trying to remove a specific item from my array, however, my array contains other objects which I cannot get a handle to.
I am defining a "Station" like this:
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
So I added a station to my array by using this function ...
.
.
self.addStation(new Station(76, "Receiving", 0, 10));
Now, I want to know how to remove from array by passing the name, as in:
self.removeStation("Receiving");
I can't figure it out. I have researched all the links on here, with no luck.
Entire Source Code
// CLASS Workflow
var Workflow = function(id, status){
this.status = status || false;
this.id = id;
}
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.station = ko.observableArray();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status=false) {
var id;
if(!workFlowId){
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station){
self.station.push(station);
}
/* Remove Station - */
self.removeStation = function (Name) {
for( var i = 0; i < self.station().length; i++){
console.dir("In Remove Function: " + self.station()[i]);
}
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function(){
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStation(new Station(76, "Receiving", 0, 10));
// 2. Remove
self.removeStation("Receiving");
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel();
// Away we go...
ko.applyBindings(window.view_model);
I can't seem to get a hold of the name in the array:
// DON'T WORK
self.station()[i].Station.name
Thanks for looking.
John
u could use function to find its index look like:
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
then in your /* Remove Station - */u edit code look like this:
/* Remove Station - */
self.removeStation = function (Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name === Name;
});
index > -1 && self.station.splice(index, 1);
}
Hope this help !
you can use ko.utils.arrayRemoveItem to remove an item from the array you need to find it first
/* Remove Station */
self.removeStation = function (search) {
// find the station
var foundStation = ko.utils.arrayFirst(this.station, function(item) {
return ko.utils.stringStartsWith(item.name().toLowerCase(), search);
});
// remove the station
ko.utils.arrayRemoveItem(this.station, foundStation);
}
Thank you for your responses. But I found an acceptable solution to my problem by using this following code:
/* Remove Station */
self.removeStation = function (Name) {
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = item.name();
if(value==Name){
console.log("found it! at index " + c);
self.station().splice(c,1);
}
});
// Force the UI to update
self.station.valueHasMutated();
}
I realize this is not the cleanest or most efficient solution, but it seems to work. I would love to know how to optimize this, but that's above my pay grade, lol.
sry for long days answer, i think the problem you got is about variable you declared and variable you use not the same type, and you not use any convert of them when u assigned to your object look like:
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId()); //observalbe declared
this.name = ko.observable(name);//observalbe declared
this.posX = ko.observable(posX);//observalbe declared
this.posY = ko.observable(posY);//observalbe declared
};
but then you use assign look like
var newObj = {
id: "82",
name: "Updated Name",
posX: 92,
posY: 88
}
self.modifyStation("name","Shipping",newObj);
when u assign it:
objNewItem.id = oldId; // Put old ID back in
self.station.push(objNewItem); //not convert its properties to observable will make some errors on binding html.
i make a working jsfildle with your example, please read it, and reply when u not understand which part. hope this help.
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
// CLASS Workflow
var Workflow = function(id, status) {
this.status = status || false;
this.id = id;
}
/* CLASS Field
* #param id int unique id this station
* #param fieldType str type of input
* #param fieldName obj name of the input
* #param options array options array
*/
var Field = function(fieldId, fieldType, fieldName, options) {
this.fieldId = ko.observable(fieldId);
this.fieldType = ko.observable(fieldType);
this.fieldName = ko.observable(fieldName);
this.options = ko.observableArray(options);
};
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX = null, posY = null, fields) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
this.fields = ko.observableArray(fields || []);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.fieldId = ko.observable();
self.fieldName = ko.observable();
self.fieldType = ko.observable();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
self.station = ko.observableArray();
self.fields = ko.observableArray();
/*******************************
* Computed Observables
*-----------------------------*/
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status = false) {
var id;
if (!workFlowId) {
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station) {
self.station.push(station);
}
/* Remove Station */
self.removeStation = function(Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name() === Name;
});
index > -1 && self.station.splice(index, 1);
}
/* Update A Station ** NEEDS FIXING **
*
* #param findBy string Property to find ( "id" or "name")
* #param cmpVal string Value to compare against
* #param objNewItem object The new object replacing old
* */
self.modifyStation = function(findBy, cmpVal, objNewItem) {
var sourceIndex;
var oldId;
var found = false;
/* Find Index Of Old Station */
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
switch (findBy) {
case "id":
var value = ko.unwrap(item.id);
if (value == cmpVal) {
sourceIndex = c;
oldId = value;
found = true;
}
break;
case "name":
var value = ko.unwrap(item.name);
if (value == cmpVal) {
sourceIndex = c;
oldId = ko.unwrap(item.id);
found = true;
}
break;
}
});
/* Remove Old */
if (found === true) {
self.station().splice(sourceIndex, 1);
/* Insert New Station
* [For Now] not allowing updating of ID. Only
* can update the other properties (yes, I realize that
* only leaves "name", but more will be added )
*/
objNewItem.id(oldId); // Put old ID back in
self.station.push(objNewItem);
} else {
alert(cmpVal + " was not found in array!");
}
}
self.addField = function(stationId, newField) {
var c = -1;
found = false;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = ko.unwrap(item.id);
console.log(value, c);
if (value == stationId) {
//console.log("found it! at index " + c);
self.station()[c].fields.push(newField);
}
});
self.station.valueHasMutated();
};
self.modifyField = function(stationId, oldFieldId, newObjField) {
// TO DO
};
self.removeField = function(field) {
self.fields.remove(field);
};
/* Perform Test On Button Click */
self.doTest = function() {
self.removeStation("Shipping");
}
self.doTest2 = function() {
var newObj = {
id: ko.observable("82"),
name: ko.observable("Updated Name"),
posX: ko.observable(92),
posY: ko.observable(88),
fields: ko.observableArray([])
}
self.modifyStation("name", "Shipping", newObj);
self.station.valueHasMutated();
}
// Add Fields
self.doTest3 = function() {
var objNewField = {
fieldId: 456,
fieldName: "Last Name",
fieldType: "Text"
}
self.addField(86, objNewField);
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function() {
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/*-------------------------------------------------------
* Method: debugAll
*
* Desc: Console Logs Our View Model
*--------------------------------------------------------*/
this.debugAll = function() {
console.group("Debug:");
// Workflow
console.group("Current Workflow Id")
console.log("self.workflowId = " + self.workflowId);
console.groupEnd("Current Workflow Id")
// Stations
console.group("Stations In This Workflow")
console.table(self.station());
console.groupEnd("Stations In This Workflow")
// Fields
console.groupEnd("Debug:");
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStationShipping = function() {
self.addStation(new Station(86, "Shipping", 0, 10, [{
fieldId: 45,
fieldName: "First Name",
fieldType: "Text"
}]));
}
self.addStation(new Station(76, "Receiving", 0, 10, null));
self.addStationShipping();
/* Dump ViewModel */
self.debugAll();
//----------------------------------------------------------------
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel(1213131212);
// Away we go...
ko.applyBindings(window.view_model);
// Page Utility Functions
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<ul data-bind="foreach: station">
<li data-bind="text: 'id: ' + id()"></li>
<li data-bind="text: 'name: ' +name()"></li>
<li data-bind="text: 'posX: ' +posX()"></li>
<li data-bind="text: 'posY: ' +posY()"></li>
<ul data-bind="foreach: fields">
<li data-bind="text: fieldId"></li>
<li data-bind="text: fieldName"></li>
<li data-bind="text: fieldType"></li>
</ul>
</ul>
</div>
<button data-bind="click: doTest">Remove "Shipping" From Station Array</button>
<button data-bind="click: doTest2">Update "Shipping" In Station Array</button>
<hr>
<h3>Test: Add Field To Shipping Station</h3>
<button data-bind="click: function() { addStationShipping()}">Add Shpping</button>
<button data-bind="click: doTest3()">Add Field</button>

jQuery - code repetition

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.

Calculate the key id of public key using openpgp.js

Using OpenPGP.js v1.3.0 I can successfully create a public/private key pair and encrypt/decrypt ok.
I am struggling to obtain the key id using this function (found in openpgp.js and public_key.js):
/**
* Calculates the key id of the key
* #return {String} A 8 byte key id
*/
PublicKey.prototype.getKeyId = function () {
if (this.keyid) {
return this.keyid;
}
this.keyid = new type_keyid();
if (this.version == 4) {
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
} else if (this.version == 3) {
this.keyid.read(this.mpi[0].write().substr(-8));
}
return this.keyid;
};
PublicKey() also in openpgp.js:
/**
* #constructor
*/
function PublicKey() {
this.tag = enums.packet.publicKey;
this.version = 4;
/** Key creation date.
* #type {Date} */
this.created = new Date();
/** A list of multiprecision integers
* #type {module:type/mpi} */
this.mpi = [];
/** Public key algorithm
* #type {module:enums.publicKey} */
this.algorithm = 'rsa_sign';
// time in days (V3 only)
this.expirationTimeV3 = 0;
/**
* Fingerprint in lowercase hex
* #type {String}
*/
this.fingerprint = null;
/**
* Keyid
* #type {module:type/keyid}
*/
this.keyid = null;
}
and trying to get the key id like this:
var publickey = openpgp.key.readArmored(myPublicKey);
//var keyID = openpgp.packet.PublicKey(publickey).getKeyId()[0].toHex();
var keyID = openpgp.PublicKey(publickey).getKeyId()[0].toHex();
console.log(keyID);
which gives me the error:
TypeError: openpgp.PublicKey is not a function.
Thanks.
I asked the question here:
http://www.mail-archive.com/list#openpgpjs.org/msg00932.html
and received a very helpful reply.
var openpgp = window.openpgp;
var testPublicKey = sessionStorage.getItem('testPublicKey');
var foundKeys = openpgp.key.readArmored(testPublicKey).keys;
if (!foundKeys || foundKeys.length !== 1) {
throw new Error("Key not read, or more than one key found");
}
var pubKey = foundKeys[0]; foundKeys = null;
var getFingerprint = function (key) {
// openpgp.key <- Class
// key <- Instance received by params
return key.primaryKey.fingerprint;
};
var getKeyId = function (key) {
return key.primaryKey.getKeyId().toHex();
}
console.log(getFingerprint(pubKey));
console.log(getKeyId(pubKey));

AngularJS - orderBy distance function

I'm extremely new to AngularJS so go easy on me... :-) I am in the process of building a new PhoneGap app with Ionic Framework and AngularJS. I have a list of locations that outputs in a list view and a function that will look up the user's location and get the distance between their current location and the location in the list. These currently both work correctly and I can see my list, sort by normal fields (name, etc).
What I would like to do is to have a preferences screen where the user will be able to set their preferred sorting option. I have already setup my basic preference controller as well that currently only is storing the preference to sort by 'name' but I'd like it to sort by distance that is calculated by a function seen below. But since the distance function seems to be in this controller, I don't know how to make it sort by that? Do I need to make a filter that runs the distance function?
Again, I'm new so any help would be greatly appreciated!
Here is my controller:
.controller('LocationsCtrl', function($scope, $ionicLoading, $ionicPopup, LocationsService, SettingsService) {
$scope.locations = {};
$scope.navTitle = "List of Locations";
$scope.rightButtons = [{
type: 'button-icon button-clear ion-more',
tap: function(e) {
$scope.openSortModal();
}
}];
// Method called on infinite scroll
// Receives a "done" callback to inform the infinite scroll that we are done
$scope.loadMore = function() {
$timeout(function() {
// Placeholder for later
$scope.$broadcast('scroll.infiniteScrollComplete');
}, 1000);
}
$scope.loading = $ionicLoading.show({
content: 'Getting current location...',
showBackdrop: false
});
navigator.geolocation.getCurrentPosition(function(pos) {
var coords = $scope.currentLocation = [pos.coords.longitude, pos.coords.latitude];
$scope.locations = LocationsService.allSync();
$scope.sortLoc = SettingsService.get('sortLocBy');
$ionicLoading.hide();
}, function(error) {
$ionicPopup.alert({
title: 'Unable to get location: ' + error.message
}).then(function(res) {
$ionicLoading.hide();
// not working
});
});
$scope.distanceFromHere = function (_item, _startPoint) {
var start = null;
var radiansTo = function (start, end) {
var d2r = Math.PI / 180.0;
var lat1rad = start.latitude * d2r;
var long1rad = start.longitude * d2r;
var lat2rad = end.latitude * d2r;
var long2rad = end.longitude * d2r;
var deltaLat = lat1rad - lat2rad;
var deltaLong = long1rad - long2rad;
var sinDeltaLatDiv2 = Math.sin(deltaLat / 2);
var sinDeltaLongDiv2 = Math.sin(deltaLong / 2);
// Square of half the straight line chord distance between both points.
var a = ((sinDeltaLatDiv2 * sinDeltaLatDiv2) +
(Math.cos(lat1rad) * Math.cos(lat2rad) *
sinDeltaLongDiv2 * sinDeltaLongDiv2));
a = Math.min(1.0, a);
return 2 * Math.asin(Math.sqrt(a));
};
if ($scope.currentLocation) {
start = {
longitude: $scope.currentLocation[0],
latitude: $scope.currentLocation[1]
};
}
start = _startPoint || start;
var end = {
longitude: _item.location.lng,
latitude: _item.location.lat
};
var num = radiansTo(start, end) * 3958.8;
return Math.round(num * 100) / 100;
}
})
And here is my template:
<ion-view title="{{navTitle}}" left-buttons="leftButtons">
<ion-content header-shrink scroll-event-interval="5">
<ion-list class="locations-list">
<ion-item class="location-item" ng-repeat="loc in locations | orderBy:sortLoc" type="item-text-wrap" href="#/location/{{loc.id}}/details" style="background-image:url('{{loc.photos.0.url}}');">
<div class="location-title">
<p>{{loc.name}}</p>
<span class="distance-indicator"><span class="digit">{{distanceFromHere(loc)}}</span><span class="unit">mi</span></span>
</div>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
See the orderBy docs. As the orderBy expression, you can use a function which generates a value to use for sorting. All you should need to do is to put the distanceFromHere function in your sortLoc scope variable (or change the filter to orderBy:distanceFromHere).

some problem when using jsdoc

'use strict';
/**
* ImagePreloader is a tool for multi-images preloading
*
* #example
* new ImagePreloader(['http://image1.jpg', 'http://image2.jpg'], function (oImage, this.aImages, this.nProcessed, this.nLoaded) {
* //use jQuery
* $('<img>').attr('src', oImage.src);
* });
*
* #author nhnst Liuming
* #version 20110623.4
* #constructor
* #param {String[]} images A collection of images url for preloading
* #param {Function} callback(oImage, this.aImages, this.nProcessed, this.nLoaded) A function to call once the every image request finishes
*/
function ImagePreloader(images, callback) {
this.callback = callback;
this.nLoaded = 0;
this.nProcessed = 0;
this.aImages = [];
this.nImages = images.length;
for (var i = 0, len = images.length ; i < len ; i += 1) {
this.preload(images[i], i);
}
}
/**
* Initialization
* #private
*/
ImagePreloader.prototype.preload = function (image, index) {
var oImage = new Image;
oImage.nIndex = index;
oImage.bLoaded = false;
oImage.oImagePreloader = this;
oImage.src = image + '?flush=' + (+new Date());
this.aImages.push(oImage);
// handle ie cache
if (oImage.width > 0) {
this.onload.call(oImage);
}
else {
oImage.onload = this.onload;
oImage.onerror = this.onerror;
oImage.onabort = this.onabort;
}
};
/**
* A inner function to be called when every image request finishes
* #private
*/
ImagePreloader.prototype.onComplete = function (oImage) {
this.nProcessed++;
this.callback(oImage, this.aImages, this.nProcessed, this.nLoaded);
};
/**
* A inner function to be called when image loads successful
* #private
*/
ImagePreloader.prototype.onload = function () {
this.bLoaded = true;
this.oImagePreloader.nLoaded++;
this.oImagePreloader.onComplete(this);
};
/**
* A inner function to be called when an error occurs loading the image
* #private
*/
ImagePreloader.prototype.onerror = function () {
this.bError = true;
this.oImagePreloader.onComplete(this);
};
/**
* A inner function to be called when image loading is aborted
* #private
*/
ImagePreloader.prototype.onabort = function () {
this.bAbort = true;
this.oImagePreloader.onComplete(this);
};
when I generate doc file using jsdoc, some warnnings occur
>> WARNING: Trying to document ImagePreloader#onload without first documenting I
magePreloader.
>> WARNING: Trying to document ImagePreloader#onerror without first documenting
ImagePreloader.
>> WARNING: Trying to document ImagePreloader#onabort without first documenting
ImagePreloader.
3 warnings.
why?
I would suggest adding #name and #class to ImagePreloader:
'use strict';
/**
* #class ImagePreloader is a tool for multi-images preloading
* #name ImagePreloader
*
* #example
* new ImagePreloader(['http://image1.jpg', 'http://image2.jpg'], function (oImage, this.aImages, this.nProcessed, this.nLoaded) {
* //use jQuery
* $('<img>').attr('src', oImage.src);
* });
*
* #author nhnst Liuming
* #version 20110623.4
* #constructor
* #param {String[]} images A collection of images url for preloading
* #param {Function} callback(oImage, this.aImages, this.nProcessed, this.nLoaded) A function to call once the every image request finishes
*/
function ImagePreloader(images, callback) {
this.callback = callback;
this.nLoaded = 0;
this.nProcessed = 0;
this.aImages = [];
this.nImages = images.length;
for (var i = 0, len = images.length ; i < len ; i += 1) {
this.preload(images[i], i);
}
}

Categories