My requirement is create a pdf file of dynamic web page. - blob

My requirement is create a pdf file of dynamic web page. web content always change based on user input.so solution is to take a snapshot of webpage on client side and convert it into pdf. I have a client side script which take the snapshot
here is the script
(function (exports) {
function urlsToAbsolute(nodeList) {
if (!nodeList.length) {
return [];
}
var attrName = 'href';
if (nodeList[0].__proto__ === HTMLImageElement.prototype
|| nodeList[0].__proto__ === HTMLScriptElement.prototype) {
attrName = 'src';
}
nodeList = [].map.call(nodeList, function (el, i) {
var attr = el.getAttribute(attrName);
if (!attr) {
return;
}
var absURL = /^(https?|data):/i.test(attr);
if (absURL) {
return el;
} else {
return el;
}
});
return nodeList;
}
function screenshotPage() {
urlsToAbsolute(document.images);
urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
var screenshot = document.documentElement.cloneNode(true);
var b = document.createElement('base');
b.href = document.location.protocol + '//' + location.host;
var head = screenshot.querySelector('head');
head.insertBefore(b, head.firstChild);
screenshot.style.pointerEvents = 'none';
screenshot.style.overflow = 'hidden';
screenshot.style.webkitUserSelect = 'none';
screenshot.style.mozUserSelect = 'none';
screenshot.style.msUserSelect = 'none';
screenshot.style.oUserSelect = 'none';
screenshot.style.userSelect = 'none';
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();';
screenshot.querySelector('body').appendChild(script);
var blob = new Blob([screenshot.outerHTML], {
type: 'text/html'
});
return blob;
}
function addOnPageLoad_() {
window.addEventListener('DOMContentLoaded', function (e) {
var scrollX = document.documentElement.dataset.scrollX || 0;
var scrollY = document.documentElement.dataset.scrollY || 0;
window.scrollTo(scrollX, scrollY);
});
}
function generate() {
window.URL = window.URL || window.webkitURL;
window.open(window.URL.createObjectURL(screenshotPage()));
}
exports.screenshotPage = screenshotPage;
exports.generate = generate;
})(window);
which return me a blob object. already try mostly client side script which available on StackOverFlow or on net
Please give me any solution to convert blob object into pdf file.

Related

Trello API add base64 data image as an attachement

I'd like to send base64 image as an attachment to a trello card through the API
POST /1/cards/[card id or shortlink]/attachments
There's a file field but it does not specify how should look the data there.
Refs: https://developers.trello.com/advanced-reference/card#post-1-cards-card-id-or-shortlink-attachments
Any idea?
Short answer: Trello's API only works to attach binary data via multipart/form-data. Examples below.
Long answer:
The Trello API to add attachments and images is frustratingly under-documented. They do have a coffeescript Client.js for those of us using Javascript to simplify all the basic operations: https://trello.com/1/client.coffee
Using the vanilla Client.js file I have been able to attach links and text files. While the CURL example shows pulling a binary file in from a hard drive, that doesn't work for those of us completely on a server or client where we don't have permissions to create a file.
From a LOT of trial and error, I've determined all binary data (images, documents, etc.) must be attached using multipart/form-data. Here is a jQuery snippet that will take a URL to an item, get it into memory, and then send it to Trello.
var opts = {'key': 'YOUR TRELLO KEY', 'token': 'YOUR TRELLO TOKEN'};
var xhr = new XMLHttpRequest();
xhr.open('get', params); // params is a URL to a file to grab
xhr.responseType = 'blob'; // Use blob to get the file's mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData(); // this is the formdata Trello needs
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
I have submitted a new coffeescript for Trello developer support to consider uploading to replace Client.js. It adds a "Trello.upload(url)" that does this work.
I've also attached here for convenience in JS form.
// Generated by CoffeeScript 1.12.4
(function() {
var opts={"version":1,"apiEndpoint":"https://api.trello.com","authEndpoint":"https://trello.com"};
var deferred, isFunction, isReady, ready, waitUntil, wrapper,
slice = [].slice;
wrapper = function(window, jQuery, opts) {
var $, Trello, apiEndpoint, authEndpoint, authorizeURL, baseURL, collection, fn, fn1, i, intentEndpoint, j, key, len, len1, localStorage, location, parseRestArgs, readStorage, ref, ref1, storagePrefix, token, type, version, writeStorage;
$ = jQuery;
key = opts.key, token = opts.token, apiEndpoint = opts.apiEndpoint, authEndpoint = opts.authEndpoint, intentEndpoint = opts.intentEndpoint, version = opts.version;
baseURL = apiEndpoint + "/" + version + "/";
location = window.location;
Trello = {
version: function() {
return version;
},
key: function() {
return key;
},
setKey: function(newKey) {
key = newKey;
},
token: function() {
return token;
},
setToken: function(newToken) {
token = newToken;
},
rest: function() {
var args, error, method, params, path, ref, success;
method = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = parseRestArgs(args), path = ref[0], params = ref[1], success = ref[2], error = ref[3];
opts = {
url: "" + baseURL + path,
type: method,
data: {},
dataType: "json",
success: success,
error: error
};
if (!$.support.cors) {
opts.dataType = "jsonp";
if (method !== "GET") {
opts.type = "GET";
$.extend(opts.data, {
_method: method
});
}
}
if (key) {
opts.data.key = key;
}
if (token) {
opts.data.token = token;
}
if (params != null) {
$.extend(opts.data, params);
}
if (method === 'UPLOAD' && typeof (params) === "string" && params.length > 5) {
var xhr = new XMLHttpRequest();
xhr.open('get', params);
xhr.responseType = 'blob'; // Use blob to get the mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData();
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
} else {
return $.ajax(opts);
}
},
authorized: function() {
return token != null;
},
deauthorize: function() {
token = null;
writeStorage("token", token);
},
authorize: function(userOpts) {
var k, persistToken, ref, regexToken, scope, v;
opts = $.extend(true, {
type: "redirect",
persist: true,
interactive: true,
scope: {
read: true,
write: false,
account: false
},
expiration: "30days"
}, userOpts);
regexToken = /[&#]?token=([0-9a-f]{64})/;
persistToken = function() {
if (opts.persist && (token != null)) {
return writeStorage("token", token);
}
};
if (opts.persist) {
if (token == null) {
token = readStorage("token");
}
}
if (token == null) {
token = (ref = regexToken.exec(location.hash)) != null ? ref[1] : void 0;
}
if (this.authorized()) {
persistToken();
location.hash = location.hash.replace(regexToken, "");
return typeof opts.success === "function" ? opts.success() : void 0;
}
if (!opts.interactive) {
return typeof opts.error === "function" ? opts.error() : void 0;
}
scope = ((function() {
var ref1, results;
ref1 = opts.scope;
results = [];
for (k in ref1) {
v = ref1[k];
if (v) {
results.push(k);
}
}
return results;
})()).join(",");
switch (opts.type) {
case "popup":
(function() {
var authWindow, height, left, origin, receiveMessage, ref1, top, width;
waitUntil("authorized", (function(_this) {
return function(isAuthorized) {
if (isAuthorized) {
persistToken();
return typeof opts.success === "function" ? opts.success() : void 0;
} else {
return typeof opts.error === "function" ? opts.error() : void 0;
}
};
})(this));
width = 420;
height = 470;
left = window.screenX + (window.innerWidth - width) / 2;
top = window.screenY + (window.innerHeight - height) / 2;
origin = (ref1 = /^[a-z]+:\/\/[^\/]*/.exec(location)) != null ? ref1[0] : void 0;
authWindow = window.open(authorizeURL({
return_url: origin,
callback_method: "postMessage",
scope: scope,
expiration: opts.expiration,
name: opts.name
}), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
receiveMessage = function(event) {
var ref2;
if (event.origin !== authEndpoint || event.source !== authWindow) {
return;
}
if ((ref2 = event.source) != null) {
ref2.close();
}
if ((event.data != null) && /[0-9a-f]{64}/.test(event.data)) {
token = event.data;
} else {
token = null;
}
if (typeof window.removeEventListener === "function") {
window.removeEventListener("message", receiveMessage, false);
}
isReady("authorized", Trello.authorized());
};
return typeof window.addEventListener === "function" ? window.addEventListener("message", receiveMessage, false) : void 0;
})();
break;
default:
window.location = authorizeURL({
redirect_uri: location.href,
callback_method: "fragment",
scope: scope,
expiration: opts.expiration,
name: opts.name
});
}
},
addCard: function(options, next) {
var baseArgs, getCard;
baseArgs = {
mode: 'popup',
source: key || window.location.host
};
getCard = function(callback) {
var height, left, returnUrl, top, width;
returnUrl = function(e) {
var data;
window.removeEventListener('message', returnUrl);
try {
data = JSON.parse(e.data);
if (data.success) {
return callback(null, data.card);
} else {
return callback(new Error(data.error));
}
} catch (error1) {}
};
if (typeof window.addEventListener === "function") {
window.addEventListener('message', returnUrl, false);
}
width = 500;
height = 600;
left = window.screenX + (window.outerWidth - width) / 2;
top = window.screenY + (window.outerHeight - height) / 2;
return window.open(intentEndpoint + "/add-card?" + $.param($.extend(baseArgs, options)), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
};
if (next != null) {
return getCard(next);
} else if (window.Promise) {
return new Promise(function(resolve, reject) {
return getCard(function(err, card) {
if (err) {
return reject(err);
} else {
return resolve(card);
}
});
});
} else {
return getCard(function() {});
}
}
};
ref = ["GET", "PUT", "POST", "DELETE", "UPLOAD"];
fn = function(type) {
return Trello[type.toLowerCase()] = function() {
return this.rest.apply(this, [type].concat(slice.call(arguments)));
};
};
for (i = 0, len = ref.length; i < len; i++) {
type = ref[i];
fn(type);
}
Trello.del = Trello["delete"];
ref1 = ["actions", "cards", "checklists", "boards", "lists", "members", "organizations", "lists"];
fn1 = function(collection) {
return Trello[collection] = {
get: function(id, params, success, error) {
return Trello.get(collection + "/" + id, params, success, error);
}
};
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
collection = ref1[j];
fn1(collection);
}
window.Trello = Trello;
authorizeURL = function(args) {
var baseArgs;
baseArgs = {
response_type: "token",
key: key
};
return authEndpoint + "/" + version + "/authorize?" + $.param($.extend(baseArgs, args));
};
parseRestArgs = function(arg) {
var error, params, path, success;
path = arg[0], params = arg[1], success = arg[2], error = arg[3];
if (isFunction(params)) {
error = success;
success = params;
params = {};
}
path = path.replace(/^\/*/, "");
return [path, params, success, error];
};
localStorage = window.localStorage;
if (localStorage != null) {
storagePrefix = "trello_";
readStorage = function(key) {
return localStorage[storagePrefix + key];
};
writeStorage = function(key, value) {
if (value === null) {
return delete localStorage[storagePrefix + key];
} else {
return localStorage[storagePrefix + key] = value;
}
};
} else {
readStorage = writeStorage = function() {};
}
};
deferred = {};
ready = {};
waitUntil = function(name, fx) {
if (ready[name] != null) {
return fx(ready[name]);
} else {
return (deferred[name] != null ? deferred[name] : deferred[name] = []).push(fx);
}
};
isReady = function(name, value) {
var fx, fxs, i, len;
ready[name] = value;
if (deferred[name]) {
fxs = deferred[name];
delete deferred[name];
for (i = 0, len = fxs.length; i < len; i++) {
fx = fxs[i];
fx(value);
}
}
};
isFunction = function(val) {
return typeof val === "function";
};
wrapper(window, jQuery, opts);
}).call(this);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>

Knockout JS - foreach: data-bind not get refreshed

SPEC : Building a customer support application.
First time, when a user send a message able to save to DB and show in the view without any issues. And from my end, if i send I am able to push the data to the UI as well DB and the user is able to get the message.
But the issue is when the user send back response again, its not showing up refreshing the UI.
Below is my knockout code,
var csendpoints = {
"Active": "/cs/oc/",
"ActiveClientData": "/cs/gnm/",
"Archive": "/cw/archive/",
"Dequeue": function (id) { return "/cw/dequeue/" + id; },
"GetQueue": "/cs/q/",
"GetArchive": "/cs/h/",
"GetActive": "/cs/active/",
"RecentlyQueued": "/cs/rq/",
"Send": "/cs/send",
"SitRep": "/cw/sitrep",
};
var customerSupportViewModel;
function PagedViewModels(model) {
var self = this;
self.Items = ko.observableArray(model.Items || []);
self.MetaData = ko.observable(model.MetaData || "");
}
function PagedListMetaDataViewModel(model) {
var self = this;
self.FirstItemOnPage = ko.observable(model.FirstItemOnPage || "");
self.HasNextPage = ko.observable(model.HasNextPage || "");
self.HasPreviousPage = ko.observable(model.HasPreviousPage || "");
self.IsFirstPage = ko.observable(model.IsFirstPage || "");
self.IsLastPage = ko.observable(model.IsLastPage || "");
self.LastItemOnPage = ko.observable(model.LastItemOnPage || "");
self.PageCount = ko.observable(model.PageCount || "");
self.PageNumber = ko.observable(model.PageNumber || "");
self.PageSize = ko.observable(model.PageSize || "");
self.TotalItemCount = ko.observable(model.TotalItemCount || "");
}
function ConversationMessageViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientContent = ko.observable(model.ClientContent || "");
self.Content = ko.observable(model.Content || "");
self.ConversationMessageId = ko.observable(model.ConversationMessageId || "");
self.dummy = ko.observable();
self.Sender = ko.observable(model.Sender || "");
self.SentOn = ko.observable(model.SentOn || "");
self.SentOnUniversalSortable = ko.observable(model.SentOnUniversalSortable || "");
self.SentTimeFromNow = ko.computed(function () {
self.dummy();
return moment.utc(self.SentOnUniversalSortable()).fromNow();
});
function updateTimeFromNow() {
setTimeout(function () {
self.dummy.notifySubscribers();
updateTimeFromNow();
},
60 * 1000);
}
updateTimeFromNow();
}
function ConversationViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientNameInitial = ko.observable(model.ClientName.substring(0, 1).toUpperCase());
self.ClientContent = ko.observable(model.ClientContent || "");
self.ConversationId = ko.observable(model.ConversationId || "");
self.CreatedOn = ko.observable(model.CreatedOn || "");
self.MessagePreview = ko.observable(model.MessagePreview || "");
self.Messages = ko.observableArray([]);
self.ProfileImageUrl = ko.observable(model.ProfileImageUrl || "");
self.Sender = ko.observable(model.Sender || "");
self.SupportName = ko.observable(customerSupportViewModel.CustomerSupportName());
self.TextBoxContent = ko.observable("");
self.TextBoxEnabled = ko.observable(true);
self.MakeActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
customerSupportViewModel.CurrentConversations.push(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
};
self.MakeRemoveMultiActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
//customerSupportViewModel.CurrentConversations.push(self);
//customerSupportViewModel.CurrentConversations.remove(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
$("#chat-wrap").mCustomScrollbar("destroy");
$("#chat-wrap").height($(window).height() - 314);
$("#chat-wrap")
.mCustomScrollbar({
axis: "y",
scrollEasing: "linear",
scrollButtons: { enable: true },
theme: "dark-thick",
scrollbarPosition: "inside",
mouseWheelPixels: 400
});
initMap()
};
self.Archive = function () {
customerSupportViewModel.ArchivedConversations.push(self);
customerSupportViewModel.ActiveConversation('');
customerSupportViewModel.CurrentConversations.remove(self);
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.RefreshWaiting();
customerSupportViewModel.RefreshActive();
customerSupportViewModel.RefreshArchive();
$.ajax(csendpoints.Archive + self.ConversationId(),
{
success: console.log("Debug: Now Archived")
});
};
self.SendMessage = function (data) {
self.TextBoxEnabled(false);
var msj = {
"SentOn": moment.utc().format(),
"SentOnUniversalSortable": moment().format(),
"Sender": 1,
"SenderName": customerSupportViewModel.CustomerSupportName(),
"Content": self.TextBoxContent(),
"ClientId": self.ClientId(),
"ClientName": self.ClientName()
};
self.Messages.push(new ConversationMessageViewModels(msj));
$.ajax({
type: "POST",
url: csendpoints.Send,
data: $(data).serialize(),
success: function () { },
complete: function () {
self.TextBoxContent("");
self.TextBoxEnabled(true);
}
});
};
_.each(model.Messages, function (item) {
self.Messages.push(new ConversationMessageViewModels(item));
});
var sortedMessages = _.sortBy(self.Messages(),
function (iteratedItem) { return iteratedItem.SentOnUniversalSortable(); });
self.Messages(sortedMessages);
}
function CustomerSupportViewModel(model) {
var self = this;
customerSupportViewModel = self;
function getWaitingList(page) {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data) {
self.WaitingList.removeAll();
_.each(data.Items, function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
self.WaitingListPaging(data.MetaData);
}
});
}
function getActiveList(page) {
$.ajax(csendpoints.GetActive + page,
{
success: function (data) {
self.CurrentConversations.removeAll();
_.each(data.Items, function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
self.CurrentConversationsPaging(data.MetaData);
}
});
}
function getArchiveList(page) {
$.ajax(csendpoints.GetArchive + page,
{
success: function (data) {
self.ArchivedConversations.removeAll();
_.each(data.Items, function (item) {
self.ArchivedConversations.push(new ConversationViewModels(item));
});
self.ArchivedConversations(data.MetaData);
}
});
}
// Conversations in Queue(i.e. waiting), Current(i.e. active) and Archive (i.e. history)
self.WaitingList = ko.observableArray([]);
self.ArchivedConversations = ko.observableArray([]);
self.CurrentConversations = ko.observableArray([]);
// Paging models
self.WaitingListPaging = ko.observable(model.ConversationsInQueue.MetaData);
self.ArchiveListPaging = ko.observable(model.ArchivedConversations.MetaData);
self.CurrentConversationsPaging = ko.observable(model.CurrentConversations.MetaData);
// The conversation currently active.
self.ActiveConversation = ko.observable();
//setInterval(self.ActiveConversation, 500);
// The name of the WeChatAccount serving the client
self.CustomerSupportName = ko.observable(model.CustomerSupportName);
self.RefreshWaiting = function () {
var page = self.WaitingListPaging().PageNumber;
getWaitingList(page);
};
self.RefreshActive = function () {
var page = self.CurrentConversationsPaging().PageNumber;
getActiveList(page);
//getWaitingList(page);
};
self.RefreshArchive = function () {
var page = self.ArchiveListPaging().PageNumber;
//getArchiveList(page);
getWaitingList(page);
};
//self.RefreshChatActive = function () {
// getActiveList()
//};
// ###### PAGING for Waiting / Queue ######
// Forward paging for the queue / waiting list
self.NextPageWaitingList = function () {
if (self.WaitingListPaging().IsLastPage) return;
var page = self.WaitingListPaging().PageNumber + 1;
getWaitingList(page);
};
self.PreviousPageWaitingList = function (data, e) {
//console.log($(data))
//console.log((e.target) ? e.target : e.srcElement)
if (self.WaitingListPaging().IsFirstPage) return;
var page = self.WaitingListPaging().PageNumber - 1;
getWaitingList(page);
};
// ###### PAGING for Active / Current conversations ######
self.NextPageActiveList = function () {
if (self.CurrentConversations().IsLastPage) return;
var page = self.CurrentConversations().PageNumber + 1;
getActiveList(page);
};
self.PreviousPageActiveList = function () {
if (self.CurrentConversations().IsFirstPage) return;
var page = self.CurrentConversations().PageNumber - 1;
getActiveList(page);
};
// ###### PAGING for History / Archived conversations ######
self.NextPageArchiveList = function () {
if (self.ArchivedConversations().IsLastPage) return;
var page = self.ArchivedConversations().PageNumber + 1;
getArchiveList(page);
};
self.PreviousPageArchiveList = function (data, e) {
if (self.ArchivedConversations().IsFirstPage) return;
var page = self.ArchivedConversations().PageNumber - 1;
getArchiveList(page);
};
_.each(model.ConversationsInQueue.Items,
function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
_.each(model.CurrentConversations.Items,
function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
_.each(model.ArchivedConversations.Items,
function (item) { self.ArchivedConversations.push(new ConversationViewModels(item)); });
function getNewQueue(page) {
setTimeout(function () {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data, odata) { getNewQueue(); }
});
},
15 * 1000);
}
}
var customerSupportViewModel = new CustomerSupportViewModel(jsonViewModel);
window.setInterval(customerSupportViewModel.RefreshWaiting, 1000);
window.setInterval(customerSupportViewModel.RefreshActive, 200);
window.setInterval(customerSupportViewModel.Messages, 200);
ko.applyBindings(customerSupportViewModel);
var conversationMessageViewModels = new ConversationMessageViewModels(jsonViewModel);
Here MakeRemoveMultiActive is used as a click function, and in the view, it load the data what user sent as well the response sent from my end. Note, this happens only on click event.
I want to update the UI after the click event so the view is open with the data and autoupdates.
Request experts to shed some light.

Backbone - Test method in view that uses ReadFile

I have written a backbone view which takes a file object or blob as an option in instantiation and then checks that file for EXIF data, corrects orientation and resizes the image if necessary depending on the options passed in.
Within the view there is a function mainFn which takes the file object and calls all other subsequent functions.
My issue is how to I test mainFn that uses ReadFile and an image constructor?
For my test set-up I am using mocah, chai, sinon and phantomjs.
In my sample code I have removed all other functions as to not add unnecessary clutter. If you wish to see the whole view visit its github repository.
var imageUpLoad = Backbone.View.extend({
template: _.template(document.getElementById("file-uploader-template").innerHTML),
// global variables passed in through options - required
_file: null, // our target file
cb: null,
maxFileSize: null, // megabytes
maxHeight: null, // pixels - resize target
maxWidth: null, // pixels - resize target
minWidth: null, // pixels
maxAllowedHeight: null, //pixels
maxAllowedWidth: null, // pixels
// globals determined through function
sourceWidth: null,
sourceHeight: null,
initialize: function (options) {
this._file = options.file;
this.cb = options.cb;
this.maxHeight = options.maxHeight;
this.maxWidth = options.maxWidth;
this.maxFileSize = options.maxFileSize;
this.minWidth = options.minWidth;
this.maxAllowedHeight = options.maxAllowedHeight;
this.maxAllowedWidth = options.maxAllowedWidth;
},
render: function () {
this.setElement(this.template());
this.mainFn(this._file);
return this;
},
// returns the width and height of the source file and calls the transform function
mainFn: function (file) {
var fr = new FileReader();
var that = this;
fr.onloadend = function () {
var _img = new Image();
// image width and height can only be determined once the image has loaded
_img.onload = function () {
that.sourceWidth = _img.width;
that.sourceHeight = _img.height;
that.transformImg(file);
};
_img.src = fr.result;
};
fr.readAsDataURL(file);
}
});
My test set-up
describe("image-upload view", function () {
before(function () {
// create test fixture
this.$fixture = $('<div id="image-view-fixture"></div><div>');
});
beforeEach(function () {
// fake image
this.b64DataJPG = '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAA' +
'ABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEB' +
'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
'EBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEB' +
'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
'EBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEB' +
'AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBA' +
'QAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk' +
'M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1' +
'hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj' +
'pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+' +
'Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAA' +
'AAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAx' +
'EEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl' +
'8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2' +
'hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq' +
'srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8v' +
'P09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q==';
var b64toBlob = function (b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var input = b64Data.replace(/\s/g, '');
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
try{
var blob = new Blob( byteArrays, {type : contentType});
}
catch(e){
// TypeError old chrome and FF
window.BlobBuilder = window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder;
if(e.name == 'TypeError' && window.BlobBuilder){
var bb = new BlobBuilder();
bb.append(byteArrays);
blob = bb.getBlob(contentType);
}
else if(e.name == "InvalidStateError"){
// InvalidStateError (tested on FF13 WinXP)
blob = new Blob(byteArrays, {type : contentType});
}
else{
// We're screwed, blob constructor unsupported entirely
}
}
return blob;
};
this.blobJPG = b64toBlob(this.b64DataJPG, "image/jpg");
/* **************** */
this.$fixture.empty().appendTo($("#fixtures"));
this.view = new imageUpLoad({
file: this.blobJPG,
cb: function (url) {console.log(url);},
maxFileSize: 500000,
minWidth: 200,
maxHeight: 900,
maxWidth: 1000,
maxAllowedHeight: 4300,
maxAllowedWidth: 1000
});
this.renderSpy = sinon.spy(this.view, "render");
this.readFileDataStub = sinon.stub(this.view, 'readFileData');
this.resizeImageStub = sinon.stub(this.view, 'resizeImage');
this.returnDataUrlStub = sinon.stub(this.view, 'returnDataUrl');
this.mainFnSpy = sinon.spy(this.view, 'mainFn');
this.transformImgStub = sinon.stub(this.view, 'transformImg');
this.sizeConfigStub = sinon.stub(this.view, 'sizeConfig');
this.resizeConfStub = sinon.stub(this.view, 'resizeConf');
this.callbackSpy = sinon.spy();
});
afterEach(function () {
this.renderSpy.restore();
this.readFileDataStub.restore();
this.resizeImageStub.restore();
this.returnDataUrlStub.restore();
this.mainFnSpy.restore();
this.sizeConfigStub.restore();
this.resizeConfStub.restore();
this.transformImgStub.restore();
});
after(function () {
$("#fixtures").empty();
});
it("can render", function () {
var _view = this.view.render();
expect(this.renderSpy).to.have.been.called;
expect(this.view).to.equal(_view);
});
});
You could either mock the FileReader / Image on the window, e.g.
// beforeEach
var _FileReader = window.FileReader;
window.FileReader = sinon.stub().return('whatever');
// afterEach
window.FileReader = _FileReader;
Or reference the constructor on the instance, e.g.
// view.js
var View = Backbone.View.extend({
FileReader: window.FileReader,
mainFn: function() {
var fileReader = new this.FileReader();
}
});
// view.spec.js
sinon.stub(this.view, 'FileReader').return('whatever');
Personally I'd prefer the latter as there's no risk of breaking the global reference if, for example, you forget to reassign the original value.

WebRTC Play Audio Input as Microphone

I want to play my audio file as microphone input (without sending my live voice but my audio file) to the WebRTC connected user. Can anybody tell me how could it be done?
I have done some following tries in the JS code, like:
1. base64 Audio
<script>
var base64string = "T2dnUwACAAAAAAA..";
var snd = new Audio("data:audio/wav;base64," + base64string);
snd.play();
var Sound = (function () {
var df = document.createDocumentFragment();
return function Sound(src) {
var snd = new Audio(src);
df.appendChild(snd);
snd.addEventListener('ended', function () {df.removeChild(snd);});
snd.play();
return snd;
}
}());
var snd = Sound("data:audio/wav;base64," + base64string);
</script>
2. AudioBuffer
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext();
var isPlaying = false;
var sourceNode = null;
var theBuffer = null;
window.onload = function() {
var request = new XMLHttpRequest();
request.open("GET", "sounds/DEMO_positive_resp.wav", true);
request.responseType = "arraybuffer";
request.onload = function() {
audioContext.decodeAudioData( request.response, function(buffer) {
theBuffer = buffer;
} );
}
request.send();
}
function togglePlayback() {
var now = audioContext.currentTime;
if (isPlaying) {
//stop playing and return
sourceNode.stop( now );
sourceNode = null;
analyser = null;
isPlaying = false;
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
//window.cancelAnimationFrame( rafID );
return "start";
}
sourceNode = audioContext.createBufferSource();
sourceNode.buffer = theBuffer;
sourceNode.loop = true;
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
sourceNode.connect( analyser );
analyser.connect( audioContext.destination );
sourceNode.start( now );
isPlaying = true;
isLiveInput = true;
return "stop";
}
Please help me out in this case. It would be highly appreciable.
Here is a demo that may help you stream mp3 or wav using chrome:
https://www.webrtc-experiment.com/RTCMultiConnection/stream-mp3-live.html
Here is, how it is written:
http://www.rtcmulticonnection.org/docs/getting-started/#stream-mp3-live
And source code of the demo:
https://github.com/muaz-khan/RTCMultiConnection/blob/master/demos/stream-mp3-live.html
https://github.com/muaz-khan/WebRTC-Experiment/issues/222
Use in 3rd party WebRTC applications
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
var gainNode = context.createGain();
gainNode.connect(context.destination);
// don't play for self
gainNode.gain.value = 0;
document.querySelector('input[type=file]').onchange = function() {
this.disabled = true;
var reader = new FileReader();
reader.onload = (function(e) {
// Import callback function that provides PCM audio data decoded as an audio buffer
context.decodeAudioData(e.target.result, function(buffer) {
// Create the sound source
var soundSource = context.createBufferSource();
soundSource.buffer = buffer;
soundSource.start(0, 0 / 1000);
soundSource.connect(gainNode);
var destination = context.createMediaStreamDestination();
soundSource.connect(destination);
createPeerConnection(destination.stream);
});
});
reader.readAsArrayBuffer(this.files[0]);
};
function createPeerConnection(mp3Stream) {
// you need to place 3rd party WebRTC code here
}
Updated at: 5:55 PM - Thursday, August 28, 2014
Here is how to get mp3 from server:
function HTTP_GET(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.send();
xhr.onload = function(e) {
if (xhr.status != 200) {
alert("Unexpected status code " + xhr.status + " for " + url);
return false;
}
callback(xhr.response); // return array-buffer
};
}
// invoke above "HTTP_GET" method
// to load mp3 as array-buffer
HTTP_GET('http://domain.com/file.mp3', function(array_buffer) {
// Import callback function that provides PCM audio data decoded as an audio buffer
context.decodeAudioData(array_buffer, function(buffer) {
// Create the sound source
var soundSource = context.createBufferSource();
soundSource.buffer = buffer;
soundSource.start(0, 0 / 1000);
soundSource.connect(gainNode);
var destination = context.createMediaStreamDestination();
soundSource.connect(destination);
createPeerConnection(destination.stream);
});
});

how to to process result of google distance matrix api further?

i am new to programming.. i have this code which gives distance between two points but need to further multiply it by an integer say 10.. the project i am working on is abt calculating distance between two points and multiplying it with fare/Km like Rs.10/km (Indian Rupees) for the same. So if the distance is 30 km the fare would be 30*10 = Rs.300
Thanks in advance
following is the code
<script>
var map;
var geocoder;
var bounds = new google.maps.LatLngBounds();
var markersArray = [];
var origin1 = '';
var destinationA = '';
var destinationIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=D|FF0000|000000';
var originIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=O|FFFF00|000000';
function initialize() {
var opts = {
center: new google.maps.LatLng(55.53, 9.4),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map'), opts);
var fromText = document.getElementById('FAdd');
var options = {
componentRestrictions: {country: 'in'}
};var fromAuto = new google.maps.places.Autocomplete(fromText, options);
fromAuto.bindTo('bound', map);
var toText = document.getElementById('TAdd');
var toAuto = new google.maps.places.Autocomplete(toText, options);
toAuto.bindTo('bound', map);
geocoder = new google.maps.Geocoder();
}
function calculateDistances() {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [document.getElementById("FAdd").value],
destinations: [document.getElementById("TAdd").value],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var outputDiv = document.getElementById('outputDiv');
outputDiv.innerHTML = '';
deleteOverlays();
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
addMarker(origins[i], false);
for (var j = 0; j < results.length; j++) {
addMarker(destinations[j], true);
outputDiv.innerHTML += results[j].distance.text + '<br>';
}
}
}
}
function addMarker(location, isDestination) {
var icon;
if (isDestination) {
icon = destinationIcon;
} else {
icon = originIcon;
}
geocoder.geocode({'address': location}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
bounds.extend(results[0].geometry.location);
map.fitBounds(bounds);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
icon: icon
});
markersArray.push(marker);
} else {
alert('Geocode was not successful for the following reason: '
+ status);
}
});
}
function deleteOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
}
}
</script>
I use an Ajax call to PHP, and haven't yet used getDistanceMatrix(), but this should be an easy fix.
First, if you know you will always only have one origin and one destination, you don't need the "for" loop in your callback function. Second, you're taking the distance text rather than the distance value.
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
[...]
} else {
deleteOverlays();
var outputDiv = document.getElementById('outputDiv'),
origin = response.originAddresses[0],
destination = response.destinationAddresses[0],
result = response.rows[0].elements[0],
distance = result.distance.value,
text = result.distance.text,
price = 10 * distance;
outputDiv.innerHTML = '<p>' + text + ': Rs.' + price + '</p>';
addMarker(origin, false);
addMarker(destination, false);
}
}
I haven't tested this, so it probably needs to be tweaked. ( See https://developers.google.com/maps/documentation/distancematrix/#DistanceMatrixResponses )