aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/closure-library/closure/goog/gears/gears.js
blob: 4d9b0e7d666efc2cc4a7a36bfae3b8c877de3192 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview This file contains functions for using Gears.
 */

// TODO(arv): The Gears team is planning to inject the Gears factory as
// google.gears.factory in the main thread as well. Currently it is only
// injected in the worker threads.


goog.provide('goog.gears');

goog.require('goog.string');


/**
 * Returns a new Gears factory object.
 * @return {Object} the Gears factory object if available or null otherwise.
 */
goog.gears.getFactory = function() {
  if (goog.gears.factory_ != undefined) {
    return goog.gears.factory_;
  }

  // In the worker threads Gears injects google.gears.factory. They also plan
  // to do this in the main thread.
  var factory = goog.getObjectByName('google.gears.factory');
  if (factory) {
    return goog.gears.factory_ = factory;
  }

  // Mozilla
  /** @preserveTry */
  try {
    var gearsFactory = /** @type {Function} */
        (goog.getObjectByName('GearsFactory'));
    return goog.gears.factory_ = new gearsFactory;
  } catch (ex) {
    // fall through
  }

  // MSIE
  /** @preserveTry */
  try {
    factory = new ActiveXObject('Gears.Factory');
    var buildInfo = factory.getBuildInfo();
    if (buildInfo.indexOf('ie_mobile') != -1) {
      factory.privateSetGlobalObject(goog.global);
    }
    return goog.gears.factory_ = factory;
  } catch (ex) {
    // fall through
  }

  return goog.gears.factory_ = goog.gears.tryGearsObject_();
};


/**
 * Attempt to create a factory by adding an object element to the DOM.
 * This covers the case for safari.
 * @return {Function} The factory, or null.
 * @private
 */
goog.gears.tryGearsObject_ = function() {
  // HACK(arv): Use square bracket notation so this can compile in an
  // environment without a DOM.
  var win = goog.getObjectByName('window');
  // Safari
  if (win && win['navigator']['mimeTypes']['application/x-googlegears']) {
    /** @preserveTry */
    try {
      var doc = win['document'];
      var factory = doc['getElementById']('gears-factory');

      // If missing, create a place for it
      if (!factory) {
        factory = doc['createElement']('object');
        factory['style']['display'] = 'none';
        factory['width'] = '0';
        factory['height'] = '0';
        factory['type'] = 'application/x-googlegears';
        factory['id'] = 'gears-factory';
        doc['documentElement']['appendChild'](factory);
      }

      // Even if Gears failed to get created we get an object element. Make
      // sure that it has a create method before assuming it is a Gears factory.
      if (typeof factory.create != 'undefined') {
        return factory;
      }
    } catch (ex) {
      // fall through
    }
  }
  return null;
};


/**
 * Cached result of getFactory
 * @type {Object|undefined}
 * @private
 */
goog.gears.factory_ = undefined;


/**
 * Whether we can create a Gears factory object. This does not cache the
 * result.
 * @return {boolean} true if we can create a Gears factory object.
 */
goog.gears.hasFactory = function() {
  if (goog.gears.hasFactory_ != undefined) return goog.gears.hasFactory_;

  // Use [] notation so we don't have to put this in externs
  var factory = goog.getObjectByName('google.gears.factory');
  if (factory || goog.getObjectByName('GearsFactory')) {
    return goog.gears.hasFactory_ = true;
  }

  // Try ActiveXObject for IE
  if (typeof ActiveXObject != 'undefined') {
    /** @preserveTry */
    try {
      new ActiveXObject('Gears.Factory');
      return goog.gears.hasFactory_ = true;
    } catch (ex) {
      return goog.gears.hasFactory_ = false;
    }
  }

  // NOTE(user): For safari we have to actually create an object element
  // in the DOM. We have to do it in hasFactory so we can reliably know whether
  // there actually is a factory, else we can get into a situation, e.g. in
  // FF 3.5.3 where the Gears add-on is disabled because it's incompatible
  // with FF but application/x-googlegears is still in the mimeTypes object.
  //
  // HACK(arv): Use object by name so this can compile in an environment without
  // a DOM.
  var mimeTypes = goog.getObjectByName('navigator.mimeTypes');
  if (mimeTypes && mimeTypes['application/x-googlegears']) {
    factory = goog.gears.tryGearsObject_();

    if (factory) {
      // Might as well cache it while we have it.
      goog.gears.factory_ = factory;
      return goog.gears.hasFactory_ = true;
    }
  }

  return goog.gears.hasFactory_ = false;
};


/**
 * Cached result of hasFactory.
 * @type {boolean|undefined}
 * @private
 */
goog.gears.hasFactory_ = undefined;


/**
 * Maximum file name length.
 * @type {number}
 * @private
 */
goog.gears.MAX_FILE_NAME_LENGTH_ = 64;


/**
 * Allow 10 characters for hash (#goog.string.hashCode).
 * @type {number}
 * @private
 */
goog.gears.MAX_FILE_NAME_PREFIX_LENGTH_ = goog.gears.MAX_FILE_NAME_LENGTH_ - 10;


/**
  * Gears only allows file names up to 64 characters, so this function shortens
  * file names to fit in this limit. #goog.string.hashCode is used to compress
  * the name. This also removes invalid characters.
  * @param {string} originalFileName Original (potentially unsafe) file name.
  * @return {string} Safe file name. If originalFileName null or empty,
  *     return originalFileName.
  * @throws {Error} If originalFileName is null, empty or contains only
  *     invalid characters.
  */
goog.gears.makeSafeFileName = function(originalFileName) {
  if (!originalFileName) {
    throw Error('file name empty');
  }

  // Safety measure since Gears behaves very badly if it gets an unexpected
  // data type.
  originalFileName = String(originalFileName);

  // TODO(user): This should be removed when the Gears code
  // gets fixed to allow for any id to be passed in. Right now
  // it fails to create a user specific database if the characters
  // sent in are non alphanumeric.
  var sanitizedFileName = originalFileName.replace(/[^a-zA-Z0-9\.\-@_]/g, '');
  if (!sanitizedFileName) {
    throw Error('file name invalid: ' + originalFileName);
  }
  if (sanitizedFileName.length <= goog.gears.MAX_FILE_NAME_LENGTH_) {
    return sanitizedFileName;
  }

  // Keep as many characters in original as we can, then hash the rest.
  var prefix = sanitizedFileName.substring(
      0, goog.gears.MAX_FILE_NAME_PREFIX_LENGTH_);
  return prefix + String(goog.string.hashCode(originalFileName));
};