|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). 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); |
|
}, |
|
|
|
/** |
|
* APIMethod: 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) { |
|
OpenLayers.Protocol.prototype.read.apply(this, arguments); |
|
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; |
|
}, |
|
|
|
/** |
|
* APIMethod: 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; |
|
}, |
|
|
|
/** |
|
* APIMethod: 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; |
|
}, |