How to access local variable outside of a function - android

Here, i am not sure why error function is not working if the C_id comming from server is incorrect. i am getting C_id from server database and passing that C_id to other server in ajax request.
$.ajax
({
url: "http://proserve.ekspeservices.com/client.php",
type: "GET",
datatype: "json",
data: {type: 'login', id: C_id},// getting C_id from server, but here if C_id is incorrect error function is not working
ContentType: "application/json",
error: function()
{
navigator.notification.alert('inCorrect Key');
},
success: function(res)
{
var simpleJson = JSON.parse(res);
myDB.transaction(function (txe1)
{
for (var i = 0; i < simpleJson.User.length; i++)
{
var Cli_id= simpleJson.User[i].id;
myDB.transaction(function (txe)
{
txe.executeSql('CREATE TABLE Client_data(Mobile integer , C_id integer, U_id integer , name text , ip integer )');
});
myDB.transaction(function (txe1)
{
var data_ins = 'INSERT INTO Client_data (Mobile,C_id,U_id) VALUES (?,?,?)';
txe1.executeSql(data_ins, [p,C_id,U_id]
,function(tx, result)
{
navigator.notification.alert('Inserted' , onSignup, 'Info', 'ok');
},
function(error)
{
navigator.notification.alert('Already Registered');
});
});
}
});
}
});

First of i must tell,you are trying to access the variable in your case its client_id whose scope is within that ftr() only.
So you need to define it globally to access it, also you need to define it as an array as you are getting multiple values at a time so you need to push in that.
Your code will be some what like this.
Also you need to call the ab() function after ftr() is finished executing as ab() output is dependent on ftr() result.So, you can go with Jquery deferred or simply call ab() within ftr() as below
var client_id = [];
function ftr()
{
myDB.transaction(function(transaction)
{
transaction.executeSql('SELECT * FROM User_data', [], function (tx, results)
{
var len = results.rows.length;
for (var i=0; i<len; i++)
{
var emp = results.rows.item(i);
client_id.push({
id: emp.C_id,
});
}
}, null);
});
ab();
}
function ab(){
console.log(client_id);
}
function onDeviceReady()
{
myDB = window.sqlitePlugin.openDatabase({name: "mydb.db", location: 'default'});
ftr();
}
Let me know if you have any queries.

Make public that variable outside of the function simple
like this
public yourVariable;

You can also try defining the variable OUTSIDE of the function, and then pass it as a parameter to the function.
This way it can be assigned a value and be used elsewhere.

Related

Firebase Trigger Razorpay Intergration for Android

I am creating an App Where user can buy coins and for that I have been trying to integrate Razorpay into my Android App since a long time now. Razorpay can directly be used in Android. It sends Success or Failure results for payment and I can act accordingly (adding points to database in this case). But the problem with this approach is that I have to write points (after success) to database from the app. Which means I have to give write access for points node to user app which is not a good idea. So I wanted to use Razorpay with Firebase Cloud Functions and searching for a long time I came across this tutorial which is for web. I am quite new to Cloud Functions and hence wanted a little help for Android.
Here is the Index.js code but For Web
const functions = require("firebase-functions");
var express = require("express");
var cors = require("cors");
var request = require("request");
const crypto = require("crypto");
const key = "----insert yout key here----";
const key_secret = "----- insert key secret here ----";
var app = express();
app.use(cors({ origin: true }));
app.post("/", (req, res) => {
const amount = req.body.amount;
//Allow Api Calls from local server
const allowedOrigins = [
"http://127.0.0.1:8080",
"http://localhost:8080",
"https://-------YourFirebaseApp-----.firebaseapp.com/"
];
const origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader("Access-Control-Allow-Origin", origin);
}
var options = {
method: "POST",
url: "https://api.razorpay.com/v1/orders",
headers: {
//There should be space after Basic else you get a BAD REQUEST error
Authorization:
"Basic " + new Buffer(key + ":" + key_secret).toString("base64")
},
form: {
amount: amount,
currency: "INR",
receipt:
"----- create a order in firestore and pass order_unique id here ---",
payment_capture: 1
}
};
request(options, function(error, response, body) {
if (error) throw new Error(error);
res.send(body);
});
});
app.post("/confirmPayment", (req, res) => {
const order = req.body;
const text = order.razorpay_order_id + "|" + order.razorpay_payment_id;
var signature = crypto
.createHmac("sha256", key_secret)
.update(text)
.digest("hex");
if (signature === order.razorpay_signature) {
console.log("PAYMENT SUCCESSFULL");
res.send("PAYMENT SUCCESSFULL");
} else {
res.send("something went wrong!");
res.end();
}
});
exports.paymentApi = functions.https.onRequest(app);
I think this will help you.
In my case, I am accessing items(Array of Product IDs) from the user's cart and reading the current price of the items then passing it as an argument to SendOrderId function which will return an OrderId to proceed.
The important thing to keep in mind is that you must have added razorpay in your dependencies inside package.json. You can do that by simply running
npm i razorpay
inside your functions folder (Which include index.js) which will automatically add the dependency to your project
const functions = require("firebase-functions");
const admin = require('firebase-admin');
const Razorpay = require('razorpay')
const razorpay = new Razorpay({
key_id: 'Your_razorpay_key_id',
key_secret: 'your_secret'
})
admin.initializeApp();
function SendOrderId(amountData, response) {
var options = {
amount: amountData, // amount in the smallest currency unit
currency: "INR",
};
razorpay.orders.create(options, function(err, order) {
console.log(order);
response.send(order);
});
}
exports.getOrderId = functions.https.onRequest((req, res) => {
return admin.firestore().collection('Users').doc(req.query.uid).get().then(queryResult => {
console.log(queryResult.data().Cart);
admin.firestore().collectionGroup("Products").where('ProductId', 'in', queryResult.data().Cart).get().then(result => {
var amount = 0;
result.forEach(element => {
amount += element.data().price;
});
SendOrderId(amount * 100, res);
})
})
});

How to invoke lambda function in android?

So i made this lambda function, and in that function I made an if statement, which creates me a user in a user table in dynamoDB. How do i call that ONLY call that if statement in my lambda function from android?
Here is my lambda function
exports.handler = function(event, context)
{
console.log('stageA');
console.log(JSON.stringify(event, null, ' '));
var responseCode = 200;
var userTableName = "usersTable";
var requestBody = event.body;
var pathParams = event.path;
var httpMethod = event.httpMethod; // HTTP Method (e.g., POST, GET, HEAD)
//User parameters
var displayName;
var email;
var fbUserID;
var firstName;
var folders;
var lastName;
var origin;
var profileImageRef;
var level;
var username;
var birthdate;
var experience;
var folder;
if (pathParams == "/user/createByEmail" && httpMethod == "POST")
{
console.log('create by email action');
requestBody = JSON.parse(requestBody);
//Set variables
firstName = requestBody.firstName;
lastName = requestBody.lastName;
email = requestBody.email;
username = requestBody.username;
experience = "0";
birthdate = requestBody.birthdate;
dynamodb.putItem(
{
"TableName": userTableName,
"Item" :
{
"displayName":{"S": username},
"email":{"S": email},
"firstName" : {"S" : firstName},
"folderNames" : {"M" : {
"My Cards": {"N": "0" }
} },
//"folders" : {"M" : {"My Cards": {}}},
"lastName" : {"S" : lastName},
"experience" : {"N" : experience},
"username": {"S": username},
"birthdate": {"S": birthdate}
}
},
function(err, data)
{ if (err) {
console.log(err);
context.done(err);
} else {
var response =
{
statusCode: responseCode,
headers:
{
"x-custom-header" : "custom header value"
},
body: JSON.stringify(username)
};
console.log('great success: %j',data);
context.succeed(response);
}
});
}
And in android, i made an interface which will be called in an AsyncTask to call my lambda function:
public interface MyInterface {
/**
* Invoke lambda function "echo". The function name is the method name
*/
#LambdaFunction
String user(String userEmail);
/**
* Invoke lambda function "echo". The functionName in the annotation
* overrides the default which is the method name
*/
#LambdaFunction(functionName = "myUserAPiFxnName")
void noUser(NameInfo nameInfo);
}
I am new to AWS and lambda, and really hope for your guide. I am not sure if i am doing this write, and hope someone has a clear cut way with steps!
Cheers!
it seems as though you would use Cognito to map user identities to IAM roles providing temporary access privileges to call the function directly. I'm not using cognito in production, nor developing Android, but my cognito does two things for you: one, it provides authentication and authorization with many federated identity services ( facebook, google, saml, oath2, etc ), and two, it provides a user access model so you don't have to program one yourself. you can pull displayName, email, birthdate from the identity service, and not have to handle logins. You can go straight to the part where you make your program.
You'd configure an identity pool in cognito and attach some identity provider(s), and invoke apis of first the identity providers and then AWS. Finally, your application would be able to invoke the lambda functions you write.
Not knowing anything about android, I'd guess there was some user identity you already had access to, probably a google identity.

IndexedDB – search for specific data? How to find the values from another value in a row?

Below is my example that I have. So far it does the following.
Enter in 3 values to a row
Retrieve all the values from all rows on load
Place all the values from each row and print them to screen that was
retrieved from the above step.
The part that I need help with is trying to find all the values of one row based on a value that exists in that row.
If a row contains the values name: ‘Andrew’ , phone: ’555-55555’, address: ’123 over there’ I would like to get all the data of that row when some one searches for ‘Andrew’
So far if you look at the function ‘searchItems’ you will see that I am trying to find data based on the value of the search input. The problem I am having is all I can get it to do is alert an “undefined” response.
How can I write the function to have it find things like described above
Also one step further. How can i edit the data that is in the row that was found based on the search input? (I have not written that function yet)
html
<!--jquery-2.1.4.js-->
<body>
<input type="text" id="dataGoesHere" />
<input type="text" id="moredataGoesHere" />
<input type="text" id="mostdataGoesHere" />
<button id="clickme">Save</button>
<div id="saved"></div>
<br><br>
<input type="text" id="searchString" />
<button id="search">search</button>
</body>
js
$(document).ready(function() {
myDb = function () {
var name = "test",
version = "1",
db,
testStoreName = "exampleStore",
testIndexName = "testIndexName",
addToTestIndex = false,
init = function () {
var deferred = $.Deferred(),
openRequest = indexedDB.open(name, version);
openRequest.onupgradeneeded = dbUpgrade;
openRequest.onsuccess = dbOpen;
openRequest.onerror = dbError;
return deferred.promise();
function dbUpgrade(evt) {
//here our db is at evt.target.result here we can adjust stores and indexes
var thisDb = evt.target.result, //the current db we are working with
newStore = thisDb.createObjectStore(testStoreName, {keyPath: "id", autoIncrement: true});
//Using indexes, you can target specific keys, this can be an array!
newStore.createIndex("testIndexKey", "testIndexKey", {unique : false});
console.log("Doing an upgrade");
}
function dbOpen(evt) {
console.log("DB successfully opened");
db = evt.target.result;
deferred.resolve();
getAllItems (function (items) {
var len = items.length;
for (var i = 0; i < len; i += 1) {
console.log(items[i]);
$('#saved').append('<p>'+items[i].item + ' ' + items[i].george + ' ' + items[i].column3 + ' ' + items[i].id + '</p>');
}
});
}
function dbError(error) {
console.log("DB Error");
console.log(error);
deferred.reject(error);
}
},
add = function(item, bob, villa) {
var itemToAdd = {"item" : item, "george" : bob, "column3" : villa},
objStore,
request,
deferred = $.Deferred();
if (!addToTestIndex) {
//here we will add half the entries to the index
//See http://stackoverflow.com/questions/16501459/javascript-searching-indexeddb-using-multiple-indexes for a better example
addToTestIndex = !addToTestIndex;
itemToAdd.testIndexKey = "This is a test";
}
//first get the object store with the desired access
objStore = db.transaction([testStoreName], "readwrite").objectStore(testStoreName);
//next create the request to add it
request = objStore.add(itemToAdd);
request.onsuccess = function (evt) {
deferred.resolve(evt.target.result);
};
request.onerror = function (evt) {
deferred.reject(evt);
};
return deferred.promise();
},
get = function (index) {
//Since our store uses an int as a primary key, that is what we are getting
//The cool part is when you start using indexes...
var deferred = $.Deferred(),
store = db.transaction([testStoreName], "readonly").objectStore(testStoreName);
request = store.get(parseInt(index));
request.onsuccess = function (evt) {
console.log(evt);
deferred.resolve(evt.target.result);
};
request.onerror = function (evt) {
deferred.reject("DBError: could not get " + index + " from " + testStoreName);
};
return deferred.promise();
},
getAllItems = function(callback) {
var trans = db.transaction(testStoreName, IDBTransaction.READ_ONLY);
var store = trans.objectStore(testStoreName);
var items = [];
trans.oncomplete = function(evt) {
callback(items);
};
var cursorRequest = store.openCursor();
cursorRequest.onerror = function(error) {
console.log(error);
};
cursorRequest.onsuccess = function(evt) {
var cursor = evt.target.result;
if (cursor) {
items.push(cursor.value);
cursor.continue();
}
};
},
searchItems = function(searchString) {
var deferred = $.Deferred(),
store = db.transaction([testStoreName], "readonly").objectStore(testStoreName);
request = store.get(searchString);
request.onerror = function(event) {
// Handle errors!
alert("error");
};
request.onsuccess = function(event) {
alert('start seach')
// Do something with the request.result!)
alert(event.target.result);
alert('end search')
};
};
return {
init: init,
get: get,
add: add,
getAllItems: getAllItems,
searchItems: searchItems
};
}
var db = new myDb();
db.init().then(function () {
$("#clickme").click(function(evt) {
//add, then we will get the added item from the db
console.log("Adding new item to db");
db.add($('#dataGoesHere').val(), $('#moredataGoesHere').val(), $('#mostdataGoesHere').val())
.then(function (res) {
return db.get(res);
})
.then(function (res) {
$('#saved').append('<p>'+res.item+' '+res.george+' '+res.column3+'</p>');
});
});
$("#search").click(function(evt) {
// search for a specific row
console.log("searching");
db.searchItems($('#searchString').val());
});
})
});
First thing (and could be the probable cause): Are you having an index on the key you are trying to search? In you case, for data as name: ‘Andrew’ , phone: ’555-55555’, address: ’123 over there’ if you trying to search for "Andrew", then you should be having an index created on "name". If it is not there then you will not be able to make a search and that's what probably could be happening.
Next: In the data store there could be more than one rows with name as Andrew, so you can have a cursor open using the index and then loop through all value and get the desired one. Something like (I am just throwing you an idea through this code snippet):
var objectStoreHandler = transaction.objectStore(selectDataObj.tblName);
var indexHandler = objectStoreHandler.index(selectDataObj.whereColObj.whereColNameArr[0]);
var cursorHandler = indexHandler.openCursor(keyRange);
cursorHandler.onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
console.log("!!!!!");
if (cursor.value != null && cursor.value != undefined) {
resultArr.push(cursor.value);
}
cursor["continue"]();
} else {
console.log("################### " + resultArr);
return successCallBack(resultArr);
}
return;
};

Phonegap/Pushwoosh Android retrieving Device id / Token

How to retrieve device id/ token at device registration? I am using Phonegap Pushwoosh example and it works fine. But I could not figure out how to retrieve the token at device registration initPushwoosh.
I am not a professional programmer. Any help will be appreciated.
I have an index.html that initialize
<body onload="init();">
In main.js
function init() {
document.addEventListener("deviceready", deviceInfo, true);
document.addEventListener("deviceready", initPushwoosh, true);
}
In PushNotification.js
function initPushwoosh()
{
var pushNotification = window.plugins.pushNotification;
// CHANGE projectid & appid
pushNotification.registerDevice({ projectid: "xxxxxxx", appid : "xxxxxxxx" },
function(status) {
var pushToken = status;
console.warn('push token: ' + pushToken);
},
function(status) {
console.warn(JSON.stringify(['failed to register ', status]));
});
document.addEventListener('push-notification', function(event) {
var title = event.notification.title;
var userData = event.notification.userdata;
if(typeof(userData) != "undefined") {
console.warn('user data: ' + JSON.stringify(userData));
}
navigator.notification.alert(title);
});
}
The first section is the .registerDevice and the token is probably pushToken, but I just cannot figure out how to retrieve it from this function!
The best is to send it to a MySQL database lets call it smartphonedb.tokentable
I modified the initPushwoosh() to send me the token to MySQL using Ajax (see below) I am receiving nothing on MySQL. Am I sending the right Token param (pushToken)?
function initPushwoosh()
{
var pushNotification = window.plugins.pushNotification;
// CHANGE projectid & appid
pushNotification.registerDevice({ projectid: "xxxxxx", appid : "xxxxxxx" },
function(status) {
var pushToken = status;
console.warn('push token: ' + pushToken);
// start my ajax to insert token to mysql
var param ={Token: pushToken};
$.ajax({
url: 'http://luxurylebanon.com/offeratlive/apitoken.php', data: param, dataType: 'json', success: function(result)
{
if(result.success == false)
{
alert(failed)
}
else {
alert(success)
}
}
});
// end ajax
},
function(status) {
console.warn(JSON.stringify(['failed to register ', status]));
});
document.addEventListener('push-notification', function(event) {
var title = event.notification.title;
var userData = event.notification.userdata;
if(typeof(userData) != "undefined") {
console.warn('user data: ' + JSON.stringify(userData));
}
navigator.notification.alert(title);
});
}
The PHP apitoken.php
<?php
$username="xxxxxxx";
$password="xxxxxxxxxxxx";
$database="offeratdb";
$server="offeratdb.db.xxxxxxxxx.com";
$connect = mysql_connect($server,$username,$password)or die('Could not connect: ' . mysql_error());
#mysql_select_db($database) or die('Could not select database ('.$database.') because of : '.mysql_error());
$vtoken= $_POST['Token'];
// Performing SQL query
$query = "INSERT INTO `tokentable` (`thetoken`) VALUES ('$vtoken')";
$result = mysql_query($query)or die('Query failed: ' . mysql_error());
echo $vtoken;
// We will free the resultset...
mysql_free_result($result);
// Now we close the connection...
mysql_close($connect);
?>
any help will be appreciated
After looking through your code I think it contains some mistakes.
So, lets try to fix them:
First of all. Do you have jquery js script included before PushNotification.js? If not, "$.ajax" will not be executed.
The other thing. The ajax default type is GET, and you use POST in your php code.
And you don't use json at all. So your code should be transformed into something like this
$.ajax({
type: "POST",
async: true,
url: url,
data: params,
success: function (result) {
// todo
},
error: function (result) {
// todo
}
});
And the last thing. The param var should be initialized like this:
var param = "Token="+pushToken;
Hope this would be helpful.
I was having the same problem, I updated the Pushwoosh.jar and it worked for me. :)

phonegap $.ajax()

anyone know how to store the jsonp data from server in phonegap local database?
the code below can help to connect the phonegap android app to the server, but how to store the data in the phonegap local database?
$.ajax({
url: 'http://172.18.75.156/deals.php',
dataType: 'jsonp',
jsonp: 'jsoncallback',
timeout: 5000,
success: function(data, status){
$.each(data, function(i,item){
output.text('successful');
});
},
error: function(){
output.text('There was an error loading the data.');
}
});
db = window.openDatabase("SQL", 3, "PhoneGap Demo", 200000);
db.transaction(ajex_call, errorCB);
function ajex_call(tx) {
$.ajax({
url: 'http://172.18.75.156/deals.php',
dataType: 'jsonp',
jsonp: 'jsoncallback',
timeout: 5000,
success: function(data, status){
$.each(data, function(i,item){
//item.obj
tx.executeSql("INSERT OR REPLACE INTO table-name(table-fields) values(?,?,..)", [array-data])
});
},
error: function(){
output.text('There was an error loading the data.');
}
});
}
More information for local database http://docs.phonegap.com/en/2.2.0/cordova_storage_storage.md.html
Try like this hope this will work:
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
db = window.openDatabase("SQL", 3, "PhoneGap Demo", 200000);
db.transaction(ajex_call, success, errorCB);
}
function ajex_call(tx) {
tx.executeSql('DROP TABLE IF EXISTS table_name');
tx.executeSql('CREATE TABLE IF NOT EXISTS table_name (fields_required_for_table)');
$.ajax({ url: 'http://172.18.75.156/deals.php', dataType: 'jsonp', jsonp: 'jsoncallback', timeout: 5000, success: function(data, status){
$.each(data, function(i,item){
tx.executeSql("INSERT OR REPLACE INTO table-name(table-fields) values(?,?,..)")
});
}, error: function(){
output.text('There was an error loading the data.');
}
});
}
function success(){
console.log('Success');
}
function error(){
console.log('error');
}
Check out HTML5's local storage.
PhoneGap's docs for it here:
I made a basic database controller class for this kind of thing a long time ago, managed to find it, hopefully it'll give you an idea.
Once you place the DataBaseCtrl code somewhere you'll be able to use it like this:
var myDatabase = DataBaseCtrl();
myDatabase.initWithConfig("DBShortName", "1.0", "MyDbName", 10000);
myDataBase.executeSql("SQL commands here...");
In your case, depending on how your data looks like you would set up your tables
myDataBase.executeSql("CREATE TABLE IF NOT EXISTS LOGS (id unique, log)");
myDataBase.executeSql("INSERT INTO LOGS (id, log) VALUES (1, 'foobar')");
myDataBase.executeSql("INSERT INTO LOGS (id, log) VALUES (2, 'logmsg')");
And maybe then use a loop to get all your data in:
for (i = 0; i < data.length; i += 1) {
myDataBase.executeSql("INSERT INTO LOGS (id, log) VALUES ("+i+", "+data[i]+")");
}
Here's the rest of the methods
myDataBase.init(); // uses set/default config
myDataBase.initWithConfig(shortName, version, displayName, maxSize);
myDataBase.executeSql(SqlCmmndString);
myDataBase.executeSqlWithCallBack(SqlCmmndString,SuccessCallbackfunction); // how you get data out
myDataBase.setInitConfig(shortName, version, displayName, maxSize);
This is the class code:
var DataBaseCtrl = function () {
if (!(this instanceof DataBaseCtrl)) {
return new DataBaseCtrl();
}
// Transaction error callback
function errorCB(tx, err) {
console.log("Error processing SQL: " + tx + tx.code + tx.message);
}
function successCB(tx, err) {
}
return {
_DB: null,
_config: {
// Default configuration
_shortName: "DefaultDataBaseName",
_version: "1.0",
_displayName: "DisplayName",
_maxSize: 65535 // in MBs
},
/* Initializer */
init: function () {
if (!window.openDatabase) {
alert("Databases are not supported on this device. \n\n ");
return false;
}
var cfg = {
shrt: this._config._shortName,
vers: this._config._version,
disp: this._config._displayName,
mxSz: this._config._maxSize
};
// Initialize the DataBase.
this._DB = window.openDatabase(cfg.shrt, cfg.vers, cfg.disp, cfg.mxSz);
},
/* Initialize with custom config */
initWithConfig: function (shortName, version, displayName, maxSize) {
this.setInitConfig(shortName, version, displayName, maxSize);
this.init();
},
/* Execute SQL command */
executeSql: function (SqlCmmnd) {
this._DB.transaction(function (tx) {
console.log("Executing SQL... " + SqlCmmnd.substring(0, 100));
tx.executeSql(SqlCmmnd);
}, errorCB, successCB);
},
/* Execute SQL with success callback */
executeSqlWithCallBack: function (SqlCmmnd, SuccessCallback) {
this._DB.transaction(function (tx) {
console.log("Executing SQL... " + SqlCmmnd.substring(0, 100));
tx.executeSql(SqlCmmnd, [], SuccessCallback);
}, errorCB, successCB);
},
/* Sets init config (call before initializing) */
setInitConfig: function (shortName, version, displayName, maxSize) {
console.log("Setting DB Config: " + displayName);
this._config = {
_shortName: shortName,
_version: version,
_displayName: displayName,
_maxSize: maxSize
};
}
};
};
Use an array to store the data from the JSON import. Then save the array to local storage.
$.ajax({
url: 'http://172.18.75.156/deals.php',
dataType: 'jsonp',
jsonp: 'jsoncallback',
timeout: 5000,
success: function(data, status){
var ArrayName = [];
$.each(data, function(i,item){
output.text('successful');
ArrayName[i] = item;
});
localStorage.setItem("jsontable",ArrayName);
},
error: function(){
output.text('There was an error loading the data.');
}
});
Then you can call that array using localStorage.GetItem("jsontable");
Then the user will be able to use the imported json table array without having to reimport.
I would suggest you to convert the object to string then save it in the localStorage.
To retrieve data, get the string from localStorage and convert it into JSON object
HTML5 localStorage

Categories

Resources