JavaScript API v1.0
You can use this API in the browser, on the page where the Pipe recording client is embedded to check the status of the recording client & recording process using control functions and events. The 1.0 version of the JavaScript API can be used with recording clients embedded using the 1.0 Embed Code.
The JavaScript API v1.0 has 3 components:
JavaScript Events API v1.0 (Desktop recorder) which will inform you about the events.
JavaScript Events API v1.0 (Mobile native recorder) which will inform you about mobile events.
JavaScript Control API v1.0 (Desktop recorder)
The JavaScript Control API allows you to control Pipe’s desktop recorder using JavaScript.
With it, you can for example create your own UI by hiding the lower menu of the desktop Pipe recorder and control the recording process through the JS Control API using your own buttons.
Controlling the Recorder Through JavaScript When Using the 1.0 Embed Code
The 1.0 version of the embed code supports only 1 recorder on the page represented by the VideoRecorder
object. You can tell it to start recording by calling the record()
method on the VideoRecorder
object like this:
<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
//wait for onRecorderReady before attaching the click event to the button above
function onRecorderReady(recorderId,recorderType){
document.getElementById("recordbtn").onclick = function (){
document.VideoRecorder.record();
}
}
</script>
Full List of Control Methods You Can Use When Using the 1.0 Embed Code
Here is the full list of methods that you can call on the Pipe recorder and that make up the JS Control API:
record();
Function
Description: This call will trigger the recorder to record a video or audio stream or to rerecord one if one is already recorded (equivalent to pressing the RECORD button).
It should be executed only after:
onCamAccess(allowed, recorderId)
is called by Pipe and the value ofallowed
istrue
ANDonRecorderReady()
is called by Pipe
If executed before the above 2 events, the initial recording screen might be shown by Pipe in which case calling record()
will do nothing.
Return Value: none
stopVideo();
Function
Description: This call will trigger the Pipe desktop client to stop the recording of a stream if a recording is in progress.
Return Value: none
playVideo();
Function
Description: This call will play the recorded stream if there is one. This will not be available during or before the recording process (equivalent to pressing the PLAY button).
Return Value: none
pause();
Function
Description: This call will pause the video recording that is currently playing. The call will not do anything if a video recording is not playing. The call is equivalent to pressing the PAUSE PLAYBACK button. Calling the function a second time will not do anything, you need to call playVideo()
to resume playback.
Return Value: none
save();
Function
Description: If recording autosaving is disabled you can use this call to save the desired recording (equivalent to pressing the Save button). This will not be available if recording autosaving is enabled.
Return Value: none
getStreamTime();
Function
Description: This will return the time of the played or the currently recording stream.
Return Value: a number with the current length of the recording (if called during the recording process)
getPlaybackTime();
Function
Description: This will return the current time of the recording during playback.
Return Value: a number with the current time of the recording if called during the playback process or 0 if called outside of playback
getStreamName();
Function
Description: This call will return the stream name at any time but beware that the name of the stream might not be set at the time of the call. This is useful when you need the name of the stream during the recording.
Return Value: a string, the stream name WITHOUT the file extension. If the stream name is not set it will return an empty string.
Removing the Pipe Recorder From Page (Embed Code 1.0)
When the desktop recorder is used and it’s part of a modal window that the user closes (either through CSS display: none
or by removing the modal DIV from the DOM) the webcam will continue to be accessed and its light will continue to be turned on.
For this scenario, we’ve built the following JS function that closes the media server connection, turns off the webcam access, and clears the contents of the #hdfvr-content
DIV returning the HTML source code to its initial state:removePipeRecorder();
.
Execute it before closing the modal window.
JavaScript Events API v1.0 (Desktop recorder)
Every time something happens with the desktop recorder (a button is pressed, the connection succeeds, etc.), the recorder will execute a specific JavaScript function with information about the event.
You can add these event functions to your HTML/JS app and extend them with your own code to do your bidding.
Here are some of the things you can do by extending these event functions:
- show a message, a warning, or a timer on the HTML page
- activate an input field or button in the HTML page
- redirect the user to another page
When using the v1 embed code you can just add these JS event functions to your HTML page. The Pipe recorder client will detect their presence and execute them when needed.
Try adding the code below immediately after your v1 Pipe recorder embed code:
<script>
function onRecorderInit(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onRecorderInit("+args.join(', ')+")");
}
</script>
An alert will pop up on your web page whenever the Pipe recording client finishes initializing. You can replace onRecorderInit(recorderId)
with any of the event functions below.
The desktop recorder has 2 different set of events and corresponding event functions:
- Event functions executed when recording a new video using the desktop recorder with embed code 1.0
- Event functions executed when uploading an existing file using the desktop recorder with embed code 1.0
Event Functions Executed When Recording a New Video Using the Desktop Recorder With Embed Code 1.0
This is the list of JavaScript event functions the Pipe client will execute when the user is recording from a desktop device using his webcam or microphone.
- onRecorderInit(recorderId)
- onRecorderReady(recorderId, recorderType)
- userHasCamMic(cam_number,mic_number, recorderId)
- btRecordPressed(recorderId)
- btStopRecordingPressed(recorderId)
- btPlayPressed(recorderId)
- btPausePressed(recorderId)
- onUploadDone(streamName, streamDuration, userId, recorderId, audioCodec, videoCodec, fileType, audioOnly, location)
- onUploadProgress(percent)
- onCamAccess(allowed, recorderId)
- onPlaybackComplete(recorderId)
- onRecordingStarted(recorderId)
- onConnectionClosed(recorderId)
- onConnectionStatus(status, recorderId)
- onMicActivityLevel(recorderId, currentActivityLevel)
- onSaveOk(streamName, streamDuration, userId, cameraName, micName, recorderId, audioCodec, videoCodec, fileType, videoId, audioOnly, location)
onRecorderInit(recorderId);
Function
Description: Triggers after the initial [Record Video] or [Record Audio] screen is drawn.
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function onRecorderInit(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onRecorderInit("+args.join(', ')+")");
}
onRecorderReady(recorderId, recorderType);
Function
Description: The Pipe recorder is ready to start recording videos (the interface has been built, you have allowed access to your webcam and you can see yourself in the browser)
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed coderecorderType
- a string representing the type of the recorder (HTML5
).
Example 1: show a browser alert when onRecorderReady
is executed
<script>
function onRecorderReady(recorderId, recorderType){
var args = Array.prototype.slice.call(arguments);
alert("onRecorderReady("+args.join(', ')+")");
}
</script>
Example 2: wait for onRecorderReady
before allowing an HTML button to trigger the recording process
<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
//wait for onRecorderReady before adding the onclick event to the button above
function onRecorderReady(recorderId,recorderType){
console.log("onRecorderReady executed")
//when the button is clicked
document.getElementById("recordbtn").onclick = function (){
//trigger the recording process
ββdocument.VideoRecorder.record();
}
}
</script>
userHasCamMic(cam_number,mic_number, recorderId);
Function
Description: Pipe detects the number of cams and mics a user has
Passed Parameters:
cam_number
- number of webcam drivers the user has installed on his computermic_number
- number of sound cards and sound sources the user has installed on his computerrecorderId
- the value of recorderId property of flashvars object in the embed code
Example:
function userHasCamMic(cam_number,mic_number, recorderId){
var args = Array.prototype.slice.call(arguments);
alert("userHasCamMic("+args.join(', ')+")");
}
btRecordPressed(recorderId);
Function
Description: RECORD button is pressed. This event does NOT mark the exact start of the recording. See onRecordingStarted.
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function btRecordPressed(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("btRecordPressed("+args.join(', ')+")");
}
btStopRecordingPressed(recorderId);
Function
Description: STOP RECORD button is pressed
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function btStopRecordingPressed(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("btStopRecordingPressed("+args.join(', ')+")");
}
btPlayPressed(recorderId);
Function
Description: PLAY button is pressed
Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function btPlayPressed(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("btPlayPressed("+args.join(', ')+")");
}
btPausePressed(recorderId);
Function
Description: PAUSE button is pressed
Example:
function btPausePressed(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("btPausePressed("+args.join(', ')+")");
}
onUploadDone(streamName, streamDuration, userId, recorderId, audioCodec, videoCodec, fileType, audioOnly, location);
Function
Description: Recorded data finishes streaming/uploading to the media server
Passed Parameters:
streamName
- a string representing the name of the stream WITHOUT the file extensionstreamDuration
- the duration of the recording/audio file in seconds but accurate to the millisecond (like this: 4.322)userId
- the userId sent via flash varsrecorderId
- the value ofrecorderId
property offlashvars
object in the embed codeaudioCodec
- the audio codec used for the recordingvideoCodec
- the video codec used for the recordingfileType
- the initial file extension for the recordingaudioOnly
- true if it is an audio-only recording, false otherwiselocation
- will take one of 4 values:- eu2-addpipe.s3.nl-ams.scw.cloud
- us1-addpipe.s3-us-west-1.amazonaws.com
- us2-addpipe.s3.us-east-1.amazonaws.com
- ca1-addpipe.s3.ca-central-1.amazonaws.com
The location
contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.
Example:
function onUploadDone(streamName, streamDuration, userId, recorderId, audioCodec, videoCodec, fileType, audioOnly,location){
var args = Array.prototype.slice.call(arguments);
alert("onUploadDone("+args.join(', ')+")");
}
onUploadProgress(percent);
Function
Description: Triggers every time upload progress is made.
Passed Parameters:
percent
- the percent at which the upload progress is at
Example:
function onUploadProgress(percent){
var args = Array.prototype.slice.call(arguments);
alert("onUploadProgress("+args.join(', ')+")");
}
onCamAccess(allowed, recorderId);
Function
Description:
onCamAccess
is triggered:
- on macOS, after clicking the Allow or Don’t Allow buttons in the operating system’s privacy dialog box that’s asking permission for camera and microphone access. The dialog box will be brought up by the operating system the first time the browser tries to access the camera or the microphone.
- after clicking the Allow or Block (Don’t Allow for Safari) buttons in the browser’s privacy dialog box that’s asking permission for camera and microphone access. The dialog box will be brought up by the browser when the recorder tries to access the camera or the microphone.
- automatically if a previous choice (Allow or Block) made by the user is persistent.
Here is an example of Chrome’s privacy dialog box:
And here is how macOS asks the user if Firefox can access the camera:
Passed Parameters:
allowed
- is true if:
- the user clicked the [Allow] button OR
- clicked [Allow] in a previous session and that choice is persistent (in which case no additional dialog box is shown).
- and false if
- the user clicked the [Block] button OR
- clicked [Block] in a previous session and that choice is persistent (in which case no additional dialog box is shown).
- is true if:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function onCamAccess(allowed, recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onCamAccess("+args.join(', ')+")");
}
onPlaybackComplete(recorderId);
Function
Description: Pipe finishes playback of recording stream
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function onPlaybackComplete(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onPlaybackComplete("+args.join(', ')+")");
}
onRecordingStarted(recorderId);
Function
Description: the Pipe desktop client started recording. This event is called ONLY if there’s data to record and, when it is called, it will be called with a 200-220ms delay after the record button is pressed and the btRecordPressed
event is triggered.
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function onRecordingStarted(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onRecordingStarted("+args.join(', ')+")");
}
onConnectionClosed(recorderId);
Function
Description:the connection to the media server has failed completely (the connection could not be reestablished even after the 30 reconnection attempts in the desktop recorder)
Passed Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed code
Example:
function onConnectionClosed(recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onConnectionClosed("+args.join(', ')+")");
}
onConnectionStatus(status, recorderId);
Function
Description: Called by the Pipe desktop client for every connection event
Passed Parameters:
status
- the actual connection status:
Desktop recorder only
- connected
- The connection is live
- disconnected: ping timeout
- When the client loses Internet connection.
- disconnected: io server disconnect
- When the media server is shut down.
- disconnected: io client disconnect
- The socket was manually disconnected.
- disconnected: transport close
- The connection was closed (for example the user has lost connection, or the network was changed from WiFi to 4G).
- disconnected: transport error
- The connection has encountered an error (for example the server was killed during an HTTP long-polling cycle)
- error
- Thrown when an error occurs.
- connect_timeout
- Thrown when connection timeouts.
- reconnecting
- Actively trying to reconnect. A total of 30 reconnect attempts will be made, each attempt at an interval between 1000 ms - 5000 ms.
- connect_error: (+ error)
- Thrown after a connection error.
- reconnect_error
- Thrown only after a reconnecting attempt when the reconnection attempt resulted in an error.
- reconnect_failed
- Reconnection has completely failed after a total number of 30 attempts were made. The client is considered fully disconnected.
- reconnected
- Thrown only after a reconnecting attempt when the reconnection was successful.
recorderId
- the value of recorderId
property of flashvars
object in the embed code
Example:
function onConnectionStatus(status, recorderId){
var args = Array.prototype.slice.call(arguments);
alert("onConnectionStatus("+args.join(', ')+")");
}
onMicActivityLevel(recorderId, currentActivityLevel);
Function
Description: the function is called by the Pipe desktop client every 10th of a second (100 milliseconds) with the current microphone level.
Parameters:
recorderId
- the value ofrecorderId
property offlashvars
object in the embed codecurrentActivityLevel
- The amount of sound the microphone is detecting in numeric values between 0 and 100. From our experience, it’s hard to get values over 50 though.
Example:
function onMicActivityLevel(recorderId, currentActivityLevel){
var args = Array.prototype.slice.call(arguments);
alert("onMicActivityLevel("+args.join(', ')+")");
}
onSaveOk(streamName, streamDuration, userId, cameraName, micName, recorderId, audioCodec, videoCodec, fileType, videoId, audioOnly, location);
Function
Description: The recording data has been fully streamed to our media servers for further processing.
This is not a confirmation that the recorded file is ready for delivery as the recording will take a few seconds to be processed (conversion to .mp4, snapshot extraction, rotation, push to storage location).
Use Pipe’s webhooks to be notified when the recording is fully processed and ready for delivery.
Passed Parameters:
streamName
- a string representing the name of the stream WITHOUT the file extensionstreamDuration
- the duration of the recorded video/audio file in seconds but accurate to the millisecond (like this: 4.322)userId
- variable sent via flash varscameraName
- the name of the webcam driver the user has selected for capturing video datamicName
- the name of the sound card/mic driver the user has selected for capturing audio datarecorderId
- the value ofrecorderId
property offlashvars
object in the embed codeaudioCodec
- the audio codec used for the recordingvideoCodec
- the video codec used for the recordingfileType
- the initial file extension for the recordingvideoId
- a string representing the recording id in Pipe’s database (same recording id that is sent through the webhook)audioOnly
- true if it is an audio-only recording, false otherwiselocation
- will take one of 4 values:- eu2-addpipe.s3.nl-ams.scw.cloud
- us1-addpipe.s3-us-west-1.amazonaws.com
- us2-addpipe.s3.us-east-1.amazonaws.com
- ca1-addpipe.s3.ca-central-1.amazonaws.com
The location
contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.
Example:
function onSaveOk(streamName, streamDuration, userId, cameraName, micName, recorderId, audioCodec, videoCodec, fileType, videoId, audioOnly,location){
var args = Array.prototype.slice.call(arguments);
alert("onSaveOk("+args.join(', ')+")");
}
Event Functions Executed When Uploading an Existing File Using the Desktop Recorder With Embed Code 1.0
Below is the list of JavaScript functions the Pipe client will execute when the user is UPLOADING an existing video using the desktop recorder. You can turn on the feature from your embed code or from the Pipe account dashboard when generating an embed code, details here.
- onDesktopVideoSelected(filename, filetype, audioOnly)
- onDesktopVideoSubmitted()
- onDesktopVideoUploadSuccess(filename, filetype, videoId, audioOnly, location)
- onDesktopVideoUploadProgress(percent)
- onDesktopVideoUploadFailed(error)
onDesktopVideoSelected(filename, filetype, audioOnly);
Function
Description: The user has selected a recording from the computer’s library and the recording is ready for upload.
Passed Parameters:
filename
- the (automatically generated) name of the recording without extensionfiletype
- the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.audioOnly
- true if we’re expecting an audio-only recording, false otherwise
Example:
function onDesktopVideoSelected(filename,filetype, audioOnly){
var args = Array.prototype.slice.call(arguments);
alert("onDesktopVideoSelected("+args.join(', ')+")");
};
onDesktopVideoSubmitted();
Function
Description: Triggered when the recording is auto-submitted.
Passed Parameters:
- None
Example:
function onDesktopVideoSubmitted(){
var args = Array.prototype.slice.call(arguments);
alert("onDesktopVideoSubmitted("+args.join(', ')+")");
}
onDesktopVideoUploadSuccess(filename, filetype, videoId, audioOnly, location);
Function
Description: The recording has finished uploading successfully.
Passed Parameters:
filename
- the automatically generated name of the recordingfiletype
- the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.videoId
- a string representing the recording id in Pipe’s database (same recording id that is sent through the webhook)audioOnly
- true if it is an audio-only recording, false otherwiselocation
- will take one of 3 values:- eu2-addpipe.s3.nl-ams.scw.cloud
- us1-addpipe.s3-us-west-1.amazonaws.com
- us2-addpipe.s3.us-east-1.amazonaws.com
- ca1-addpipe.s3.ca-central-1.amazonaws.com
The location
contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.
Example:
function onDesktopVideoUploadSuccess(filename,filetype,videoId,audioOnly,location){
var args = Array.prototype.slice.call(arguments);
alert("onDesktopVideoUploadSuccess("+args.join(', ')+")");
}
onDesktopVideoUploadProgress(percent);
Function
Description: Triggers every time upload progress is made.
Passed Parameters:
percent
- the percent at which the upload progress is at
Example:
function onDesktopVideoUploadProgress(percent){
var args = Array.prototype.slice.call(arguments);
alert("onDesktopVideoUploadProgress("+args.join(', ')+")");
}
onDesktopVideoUploadFailed(error);
Function
Description: There was an error while uploading the recording.
Passed Parameters:
error
- The error thrown when the upload failed to complete
Example:
function onDesktopVideoUploadFailed(error){
var args = Array.prototype.slice.call(arguments);
alert("onDesktopVideoUploadFailed("+args.join(', ')+")");
}
JavaScript Events API v1.0 (Mobile native recorder)
Every time something happens with the mobile native recorder (a video is starting to upload, an upload has finished, etc.) the recorder will execute a specific JavaScript function with information about the event.
You can add these mobile native recorder event functions to your HTML/JS app and extend them with your own code to do your bidding.
When using the v1.0 embed code you can just add these JS event functions to your HTML page. The Pipe mobile native recorder will detect their presence and execute them when needed.
Try adding the code below immediately after your v1 Pipe recorder embed code:
<script>
function onVideoRecorded(filename,filetype, audioOnly){
var args = Array.prototype.slice.call(arguments);
alert("onVideoRecorded("+args.join(', ')+")");
}
</script>
An alert will pop up on your web page whenever you close the iOS/Android-controlled recording UI.
You can replace onVideoRecorded(filename,filetype, audioOnly)
in the example above with any of the mobile event functions below.
- onVideoRecorded(filename, filetype, audioOnly)
- onClickUpload()
- onVideoUploadSuccess(filename, filetype, videoId, audioOnly, location)
- onVideoUploadProgress(percent)
- onVideoUploadFailed(error)
onVideoRecorded(filename, filetype, audioOnly);
Function
Description: The user has recorded a new video or chosen an existing video from the device’s library and the video is ready for upload.
Passed Parameters:
filename
- the (automatically generated) name of the recording without extensionfiletype
- the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.audioOnly
- true if we’re expecting an audio-only recording, false otherwise
Example:
function onVideoRecorded(filename,filetype, audioOnly){
var args = Array.prototype.slice.call(arguments);
alert("onVideoRecorded("+args.join(', ')+")");
onClickUpload();
Function
Description: Triggered when the upload button is clicked.
Passed Parameters:
- None
Example:
function onClickUpload(){
var args = Array.prototype.slice.call(arguments);
alert("onClickUpload("+args.join(', ')+")");
}
onVideoUploadSuccess(filename, filetype, videoId, audioOnly, location);
Function
Description: The new or existing recording has finished uploading successfully.
Passed Parameters:
filename
- the automatically generated name of the recordingfiletype
- the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.videoId
- a string representing the recording id in Pipe’s database (same recording id that is sent through the webhook)audioOnly
- true if it is an audio-only recording, false otherwiselocation
- will take one of 4 values:- eu2-addpipe.s3.nl-ams.scw.cloud
- us1-addpipe.s3-us-west-1.amazonaws.com
- us2-addpipe.s3.us-east-1.amazonaws.com
- ca1-addpipe.s3.ca-central-1.amazonaws.com
The location
contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.
Example:
function onVideoUploadSuccess(filename,filetype,videoId,audioOnly,location){
var args = Array.prototype.slice.call(arguments);
alert("onVideoUploadSuccess("+args.join(', ')+")");
}
onVideoUploadProgress(percent);
Function
Description: Triggers every time upload progress is made.
Passed Parameters:
percent
- the percent at which the upload progress is at
Example:
function onVideoUploadProgress(percent){
var args = Array.prototype.slice.call(arguments);
alert("onVideoUploadProgress("+args.join(', ')+")");
}
onVideoUploadFailed(error);
Function
Description:There was an error while uploading the recording.
Passed Parameters:
error
- The error thrown when the upload failed to complete
Example:
function onVideoUploadFailed(error){
var args = Array.prototype.slice.call(arguments);
alert("onVideoUploadFailed("+args.join(', ')+")");
}