Batch merge for rc2 of 2.7. 'svn merge -r7967:HEAD from trunk (Closes #1733) (Closes #1489) (Closes #1639) (Closes #1718) (Closes #1723) (Closes #1732) (Closes #1616) (Closes #1722)
git-svn-id: http://svn.openlayers.org/branches/openlayers/2.7@8012 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
559
lib/OpenLayers/Protocol/SQL/Gears.js
Normal file
559
lib/OpenLayers/Protocol/SQL/Gears.js
Normal file
@@ -0,0 +1,559 @@
|
||||
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
|
||||
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
||||
* full text of the license. */
|
||||
|
||||
/**
|
||||
* @requires Gears/gears_init.js
|
||||
* @requires OpenLayers/Protocol/SQL.js
|
||||
* @requires OpenLayers/Format/JSON.js
|
||||
* @requires OpenLayers/Format/WKT.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Protocol.SQL.Gears
|
||||
* This Protocol stores feature in the browser via the Gears Database module
|
||||
* <http://code.google.com/apis/gears/api_database.html>.
|
||||
*
|
||||
* The main advantage is that all the read, create, update and delete operations
|
||||
* can be done offline.
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Protocol.SQL>
|
||||
*/
|
||||
OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
|
||||
|
||||
/**
|
||||
* Property: FID_PREFIX
|
||||
* {String}
|
||||
*/
|
||||
FID_PREFIX: '__gears_fid__',
|
||||
|
||||
/**
|
||||
* Property: NULL_GEOMETRY
|
||||
* {String}
|
||||
*/
|
||||
NULL_GEOMETRY: '__gears_null_geometry__',
|
||||
|
||||
/**
|
||||
* Property: NULL_FEATURE_STATE
|
||||
* {String}
|
||||
*/
|
||||
NULL_FEATURE_STATE: '__gears_null_feature_state__',
|
||||
|
||||
/**
|
||||
* Property: jsonParser
|
||||
* {<OpenLayers.Format.JSON>}
|
||||
*/
|
||||
jsonParser: null,
|
||||
|
||||
/**
|
||||
* Property: wktParser
|
||||
* {<OpenLayers.Format.WKT>}
|
||||
*/
|
||||
wktParser: null,
|
||||
|
||||
/**
|
||||
* Property: fidRegExp
|
||||
* {RegExp} Regular expression to know whether a feature was
|
||||
* created in offline mode.
|
||||
*/
|
||||
fidRegExp: null,
|
||||
|
||||
/**
|
||||
* Property: saveFeatureState
|
||||
* {Boolean} Whether to save the feature state (<OpenLayers.State>)
|
||||
* into the database, defaults to true.
|
||||
*/
|
||||
saveFeatureState: true,
|
||||
|
||||
/**
|
||||
* Property: typeOfFid
|
||||
* {String} The type of the feature identifier, either "number" or
|
||||
* "string", defaults to "string".
|
||||
*/
|
||||
typeOfFid: "string",
|
||||
|
||||
/**
|
||||
* Property: db
|
||||
* {GearsDatabase}
|
||||
*/
|
||||
db: null,
|
||||
|
||||
/**
|
||||
* Constructor: OpenLayers.Protocol.SQL.Gears
|
||||
*/
|
||||
initialize: function(options) {
|
||||
if (!this.supported()) {
|
||||
return;
|
||||
}
|
||||
OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
|
||||
this.jsonParser = new OpenLayers.Format.JSON();
|
||||
this.wktParser = new OpenLayers.Format.WKT();
|
||||
|
||||
this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
|
||||
this.initializeDatabase();
|
||||
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: initializeDatabase
|
||||
*/
|
||||
initializeDatabase: function() {
|
||||
this.db = google.gears.factory.create('beta.database');
|
||||
this.db.open(this.databaseName);
|
||||
this.db.execute(
|
||||
"CREATE TABLE IF NOT EXISTS " + this.tableName +
|
||||
" (fid TEXT UNIQUE, geometry TEXT, properties TEXT," +
|
||||
" state TEXT)");
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: destroy
|
||||
* Clean up the protocol.
|
||||
*/
|
||||
destroy: function() {
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
|
||||
this.jsonParser = null;
|
||||
this.wktParser = null;
|
||||
|
||||
OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: supported
|
||||
* Determine whether a browser supports Gears
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean} The browser supports Gears
|
||||
*/
|
||||
supported: function() {
|
||||
return !!(window.google && google.gears);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: read
|
||||
* Read all features from the database and return a
|
||||
* <OpenLayers.Protocol.Response> instance. If the options parameter
|
||||
* contains a callback attribute, the function is called with the response
|
||||
* as a parameter.
|
||||
*
|
||||
* Parameters:
|
||||
* options - {Object} Optional object for configuring the request; it
|
||||
* can have the {Boolean} property "noFeatureStateReset" which
|
||||
* specifies if the state of features read from the Gears
|
||||
* database must be reset to null, if "noFeatureStateReset"
|
||||
* is undefined or false then each feature's state is reset
|
||||
* to null, if "noFeatureStateReset" is true the feature state
|
||||
* is preserved.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
||||
* object.
|
||||
*/
|
||||
read: function(options) {
|
||||
options = OpenLayers.Util.applyDefaults(options, this.options);
|
||||
|
||||
var feature, features = [];
|
||||
var rs = this.db.execute("SELECT * FROM " + this.tableName);
|
||||
while (rs.isValidRow()) {
|
||||
feature = this.unfreezeFeature(rs);
|
||||
if (this.evaluateFilter(feature, options.filter)) {
|
||||
if (!options.noFeatureStateReset) {
|
||||
feature.state = null;
|
||||
}
|
||||
features.push(feature);
|
||||
}
|
||||
rs.next();
|
||||
}
|
||||
rs.close();
|
||||
|
||||
var resp = new OpenLayers.Protocol.Response({
|
||||
code: OpenLayers.Protocol.Response.SUCCESS,
|
||||
requestType: "read",
|
||||
features: features
|
||||
});
|
||||
|
||||
if (options && options.callback) {
|
||||
options.callback.call(options.scope, resp);
|
||||
}
|
||||
|
||||
return resp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: unfreezeFeature
|
||||
*
|
||||
* Parameters:
|
||||
* row - {ResultSet}
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Feature.Vector>}
|
||||
*/
|
||||
unfreezeFeature: function(row) {
|
||||
var feature;
|
||||
var wkt = row.fieldByName('geometry');
|
||||
if (wkt == this.NULL_GEOMETRY) {
|
||||
feature = new OpenLayers.Feature.Vector();
|
||||
} else {
|
||||
feature = this.wktParser.read(wkt);
|
||||
}
|
||||
|
||||
feature.attributes = this.jsonParser.read(
|
||||
row.fieldByName('properties'));
|
||||
|
||||
feature.fid = this.extractFidFromField(row.fieldByName('fid'));
|
||||
|
||||
var state = row.fieldByName('state');
|
||||
if (state == this.NULL_FEATURE_STATE) {
|
||||
state = null;
|
||||
}
|
||||
feature.state = state;
|
||||
|
||||
return feature;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: extractFidFromField
|
||||
*
|
||||
* Parameters:
|
||||
* field - {String}
|
||||
*
|
||||
* Returns
|
||||
* {String} or {Number} The fid.
|
||||
*/
|
||||
extractFidFromField: function(field) {
|
||||
if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
|
||||
field = parseFloat(field);
|
||||
}
|
||||
return field;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: create
|
||||
* Create new features into the database.
|
||||
*
|
||||
* Parameters:
|
||||
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
||||
* {<OpenLayers.Feature.Vector>} The features to create in
|
||||
* the database.
|
||||
* options - {Object} Optional object for configuring the request.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
||||
* object.
|
||||
*/
|
||||
create: function(features, options) {
|
||||
options = OpenLayers.Util.applyDefaults(options, this.options);
|
||||
|
||||
var resp = this.createOrUpdate(features);
|
||||
resp.requestType = "create";
|
||||
|
||||
if (options && options.callback) {
|
||||
options.callback.call(options.scope, resp);
|
||||
}
|
||||
|
||||
return resp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: update
|
||||
* Construct a request updating modified feature.
|
||||
*
|
||||
* Parameters:
|
||||
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
||||
* {<OpenLayers.Feature.Vector>} The features to update in
|
||||
* the database.
|
||||
* options - {Object} Optional object for configuring the request.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
||||
* object.
|
||||
*/
|
||||
update: function(features, options) {
|
||||
options = OpenLayers.Util.applyDefaults(options, this.options);
|
||||
|
||||
var resp = this.createOrUpdate(features);
|
||||
resp.requestType = "update";
|
||||
|
||||
if (options && options.callback) {
|
||||
options.callback.call(options.scope, resp);
|
||||
}
|
||||
|
||||
return resp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: createOrUpdate
|
||||
* Construct a request for updating or creating features in the
|
||||
* database.
|
||||
*
|
||||
* Parameters:
|
||||
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
||||
* {<OpenLayers.Feature.Vector>} The feature to create or update
|
||||
* in the database.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
||||
* object.
|
||||
*/
|
||||
createOrUpdate: function(features) {
|
||||
if (!(features instanceof Array)) {
|
||||
features = [features];
|
||||
}
|
||||
|
||||
var i, len = features.length, feature;
|
||||
var insertedFeatures = new Array(len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
feature = features[i];
|
||||
var params = this.freezeFeature(feature);
|
||||
this.db.execute(
|
||||
"REPLACE INTO " + this.tableName +
|
||||
" (fid, geometry, properties, state)" +
|
||||
" VALUES (?, ?, ?, ?)",
|
||||
params);
|
||||
|
||||
var clone = feature.clone();
|
||||
clone.fid = this.extractFidFromField(params[0]);
|
||||
insertedFeatures[i] = clone;
|
||||
}
|
||||
|
||||
return new OpenLayers.Protocol.Response({
|
||||
code: OpenLayers.Protocol.Response.SUCCESS,
|
||||
features: insertedFeatures,
|
||||
reqFeatures: features
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: freezeFeature
|
||||
*
|
||||
* Parameters:
|
||||
* feature - {<OpenLayers.Feature.Vector>}
|
||||
* state - {String} The feature state to store in the database.
|
||||
*
|
||||
* Returns:
|
||||
* {Array}
|
||||
*/
|
||||
freezeFeature: function(feature) {
|
||||
// 2 notes:
|
||||
// - fid might not be a string
|
||||
// - getFeatureStateForFreeze needs the feature fid to it's stored
|
||||
// in the feature here
|
||||
feature.fid = feature.fid != null ?
|
||||
"" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
|
||||
|
||||
var geometry = feature.geometry != null ?
|
||||
feature.geometry.toString() : this.NULL_GEOMETRY;
|
||||
|
||||
var properties = this.jsonParser.write(feature.attributes);
|
||||
|
||||
var state = this.getFeatureStateForFreeze(feature);
|
||||
|
||||
return [feature.fid, geometry, properties, state];
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: getFeatureStateForFreeze
|
||||
* Get the state of the feature to store into the database.
|
||||
*
|
||||
* Parameters:
|
||||
* feature - {<OpenLayers.Feature.Vector>} The feature.
|
||||
*
|
||||
* Returns
|
||||
* {String} The state
|
||||
*/
|
||||
getFeatureStateForFreeze: function(feature) {
|
||||
var state;
|
||||
if (!this.saveFeatureState) {
|
||||
state = this.NULL_FEATURE_STATE;
|
||||
} else if (this.createdOffline(feature)) {
|
||||
// if the feature was created in offline mode, its
|
||||
// state must remain INSERT
|
||||
state = OpenLayers.State.INSERT;
|
||||
} else {
|
||||
state = feature.state;
|
||||
}
|
||||
return state;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: delete
|
||||
* Delete features from the database.
|
||||
*
|
||||
* Parameters:
|
||||
* features - {Array({<OpenLayers.Feature.Vector>})} or
|
||||
* {<OpenLayers.Feature.Vector>}
|
||||
* options - {Object} Optional object for configuring the request.
|
||||
* This object is modified and should not be reused.
|
||||
*
|
||||
* Returns:
|
||||
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
|
||||
* object.
|
||||
*/
|
||||
"delete": function(features, options) {
|
||||
if (!(features instanceof Array)) {
|
||||
features = [features];
|
||||
}
|
||||
|
||||
options = OpenLayers.Util.applyDefaults(options, this.options);
|
||||
|
||||
var i, len, feature;
|
||||
for (i = 0, len = features.length; i < len; i++) {
|
||||
feature = features[i];
|
||||
|
||||
// if saveFeatureState is set to true and if the feature wasn't created
|
||||
// in offline mode we don't delete it in the database but just update
|
||||
// it state column
|
||||
if (this.saveFeatureState && !this.createdOffline(feature)) {
|
||||
var toDelete = feature.clone();
|
||||
toDelete.fid = feature.fid;
|
||||
if (toDelete.geometry) {
|
||||
toDelete.geometry.destroy();
|
||||
toDelete.geometry = null;
|
||||
}
|
||||
toDelete.state = feature.state;
|
||||
this.createOrUpdate(toDelete);
|
||||
} else {
|
||||
this.db.execute(
|
||||
"DELETE FROM " + this.tableName +
|
||||
" WHERE fid = ?", [feature.fid]);
|
||||
}
|
||||
}
|
||||
|
||||
var resp = new OpenLayers.Protocol.Response({
|
||||
code: OpenLayers.Protocol.Response.SUCCESS,
|
||||
requestType: "delete",
|
||||
reqFeatures: features
|
||||
});
|
||||
|
||||
if (options && options.callback) {
|
||||
options.callback.call(options.scope, resp);
|
||||
}
|
||||
|
||||
return resp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: createdOffline
|
||||
* Returns true if the feature had a feature id when it was created in
|
||||
* the Gears database, false otherwise; this is determined by
|
||||
* checking the form of the feature's fid value.
|
||||
*
|
||||
* Parameters:
|
||||
* feature - {<OpenLayers.Feature.Vector>}
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean}
|
||||
*/
|
||||
createdOffline: function(feature) {
|
||||
return (typeof feature.fid == "string" &&
|
||||
!!(feature.fid.match(this.fidRegExp)));
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: commit
|
||||
* Go over the features and for each take action
|
||||
* based on the feature state. Possible actions are create,
|
||||
* update and delete.
|
||||
*
|
||||
* Parameters:
|
||||
* features - {Array({<OpenLayers.Feature.Vector>})}
|
||||
* options - {Object} Object whose possible keys are "create", "update",
|
||||
* "delete", "callback" and "scope", the values referenced by the
|
||||
* first three are objects as passed to the "create", "update", and
|
||||
* "delete" methods, the value referenced by the "callback" key is
|
||||
* a function which is called when the commit operation is complete
|
||||
* using the scope referenced by the "scope" key.
|
||||
*
|
||||
* Returns:
|
||||
* {Array({<OpenLayers.Protocol.Response>})} An array of
|
||||
* <OpenLayers.Protocol.Response> objects, one per request made
|
||||
* to the database.
|
||||
*/
|
||||
commit: function(features, options) {
|
||||
var opt, resp = [], nRequests = 0, nResponses = 0;
|
||||
|
||||
function callback(resp) {
|
||||
if (++nResponses < nRequests) {
|
||||
resp.last = false;
|
||||
}
|
||||
this.callUserCallback(options, resp);
|
||||
}
|
||||
|
||||
var feature, toCreate = [], toUpdate = [], toDelete = [];
|
||||
for (var i = features.length - 1; i >= 0; i--) {
|
||||
feature = features[i];
|
||||
switch (feature.state) {
|
||||
case OpenLayers.State.INSERT:
|
||||
toCreate.push(feature);
|
||||
break;
|
||||
case OpenLayers.State.UPDATE:
|
||||
toUpdate.push(feature);
|
||||
break;
|
||||
case OpenLayers.State.DELETE:
|
||||
toDelete.push(feature);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toCreate.length > 0) {
|
||||
nRequests++;
|
||||
opt = OpenLayers.Util.applyDefaults(
|
||||
{"callback": callback, "scope": this},
|
||||
options.create
|
||||
);
|
||||
resp.push(this.create(toCreate, opt));
|
||||
}
|
||||
if (toUpdate.length > 0) {
|
||||
nRequests++;
|
||||
opt = OpenLayers.Util.applyDefaults(
|
||||
{"callback": callback, "scope": this},
|
||||
options.update
|
||||
);
|
||||
resp.push(this.update(toUpdate, opt));
|
||||
}
|
||||
if (toDelete.length > 0) {
|
||||
nRequests++;
|
||||
opt = OpenLayers.Util.applyDefaults(
|
||||
{"callback": callback, "scope": this},
|
||||
options["delete"]
|
||||
);
|
||||
resp.push(this["delete"](toDelete, opt));
|
||||
}
|
||||
|
||||
return resp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: clear
|
||||
* Removes all rows of the table.
|
||||
*/
|
||||
clear: function() {
|
||||
this.db.execute("DELETE FROM " + this.tableName);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: callUserCallback
|
||||
* This method is called from within commit each time a request is made
|
||||
* to the database, it is responsible for calling the user-supplied
|
||||
* callbacks.
|
||||
*
|
||||
* Parameters:
|
||||
* options - {Object} The map of options passed to the commit call.
|
||||
* resp - {<OpenLayers.Protocol.Response>}
|
||||
*/
|
||||
callUserCallback: function(options, resp) {
|
||||
var opt = options[resp.requestType];
|
||||
if (opt && opt.callback) {
|
||||
opt.callback.call(opt.scope, resp);
|
||||
}
|
||||
if (resp.last && options.callback) {
|
||||
options.callback.call(options.scope);
|
||||
}
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
|
||||
});
|
||||
Reference in New Issue
Block a user