More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Protocol / SQL / Gears.js
blob:a/labs/openlayers/lib/OpenLayers/Protocol/SQL/Gears.js -> blob:b/labs/openlayers/lib/OpenLayers/Protocol/SQL/Gears.js
  /* 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;
  },