calatrava 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. data/.gitignore +9 -0
  2. data/.rvmrc +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +202 -0
  5. data/Plans.md +20 -0
  6. data/README.md +73 -0
  7. data/Rakefile +26 -0
  8. data/bin/calatrava +6 -0
  9. data/calatrava.gemspec +35 -0
  10. data/features/cli.feature +14 -0
  11. data/features/project.feature +48 -0
  12. data/features/step_definitions/template_steps.rb +8 -0
  13. data/features/support/env.rb +5 -0
  14. data/lib/calatrava/app.rb +30 -0
  15. data/lib/calatrava/manifest.rb +44 -0
  16. data/lib/calatrava/project.rb +218 -0
  17. data/lib/calatrava/resources_build_phase.rb +19 -0
  18. data/lib/calatrava/tasks/apache.rb +44 -0
  19. data/lib/calatrava/tasks/artifact.rb +24 -0
  20. data/lib/calatrava/tasks/assets.rb +6 -0
  21. data/lib/calatrava/tasks/automation.rb +38 -0
  22. data/lib/calatrava/tasks/bootstrap.rb +10 -0
  23. data/lib/calatrava/tasks/build.rb +1 -0
  24. data/lib/calatrava/tasks/configuration.rb +41 -0
  25. data/lib/calatrava/tasks/droid.rb +83 -0
  26. data/lib/calatrava/tasks/haml.rb +71 -0
  27. data/lib/calatrava/tasks/ios.rb +73 -0
  28. data/lib/calatrava/tasks/kernel.rb +52 -0
  29. data/lib/calatrava/tasks/precommit.rb +22 -0
  30. data/lib/calatrava/tasks/release.rb +17 -0
  31. data/lib/calatrava/tasks/shell.rb +17 -0
  32. data/lib/calatrava/tasks/web.rb +82 -0
  33. data/lib/calatrava/tasks.rb +93 -0
  34. data/lib/calatrava/template.rb +44 -0
  35. data/lib/calatrava/templates/.rvmrc.calatrava +2 -0
  36. data/lib/calatrava/templates/Gemfile.calatrava +4 -0
  37. data/lib/calatrava/templates/Rakefile +1 -0
  38. data/lib/calatrava/templates/assets/lib/ICanHaz.js +542 -0
  39. data/lib/calatrava/templates/assets/lib/underscore.js +1059 -0
  40. data/lib/calatrava/templates/assets/lib/zepto.js +1355 -0
  41. data/lib/calatrava/templates/build_env.sh +2 -0
  42. data/lib/calatrava/templates/config/environments.yml +17 -0
  43. data/lib/calatrava/templates/config/templates/env.coffee.erb +7 -0
  44. data/lib/calatrava/templates/config/templates/httpd.conf.erb +33 -0
  45. data/lib/calatrava/templates/droid/app/bridge.coffee +130 -0
  46. data/lib/calatrava/templates/droid/calatrava/ant/calatrava.xml +48 -0
  47. data/lib/calatrava/templates/droid/calatrava/calatrava-build.xml +91 -0
  48. data/lib/calatrava/templates/droid/calatrava/ivy.xml +8 -0
  49. data/lib/calatrava/templates/droid/calatrava/ivysettings.xml +12 -0
  50. data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/AndroidManifest.xml.calatrava +20 -0
  51. data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/ConversionForm.java.calatrava +27 -0
  52. data/lib/calatrava/templates/droid/calatrava/src/com/CALATRAVA_TMPL/Title.java.calatrava +23 -0
  53. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/CalatravaPage.java +13 -0
  54. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/AjaxRequestManager.java +166 -0
  55. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/AssetRepository.java +17 -0
  56. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/KernelBridge.java +25 -0
  57. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/Launcher.java +85 -0
  58. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/PageRegistry.java +225 -0
  59. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RegisteredActivity.java +86 -0
  60. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RequestLoader.java +31 -0
  61. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/bridge/RhinoService.java +212 -0
  62. data/lib/calatrava/templates/droid/calatrava/src/com/calatrava/shell/WebViewActivity.java +247 -0
  63. data/lib/calatrava/templates/droid/manifest.yml +1 -0
  64. data/lib/calatrava/templates/ios/Podfile.calatrava +5 -0
  65. data/lib/calatrava/templates/ios/manifest.yml +1 -0
  66. data/lib/calatrava/templates/ios/res/js/bridge.js +249 -0
  67. data/lib/calatrava/templates/ios/res/xibs/ProgressViewController.xib +334 -0
  68. data/lib/calatrava/templates/ios/res/xibs/WebViewController.xib +173 -0
  69. data/lib/calatrava/templates/ios/src/AppDelegate.h +8 -0
  70. data/lib/calatrava/templates/ios/src/AppDelegate.m +56 -0
  71. data/lib/calatrava/templates/ios/src/CALATRAVA_TMPL-Info.plist +45 -0
  72. data/lib/calatrava/templates/ios/src/CALATRAVA_TMPL-Prefix.pch +14 -0
  73. data/lib/calatrava/templates/ios/src/ConversionFormViewController.h +16 -0
  74. data/lib/calatrava/templates/ios/src/ConversionFormViewController.m +179 -0
  75. data/lib/calatrava/templates/ios/src/ConversionFormViewController.xib +357 -0
  76. data/lib/calatrava/templates/ios/src/en.lproj/InfoPlist.strings +2 -0
  77. data/lib/calatrava/templates/ios/src/main.m +10 -0
  78. data/lib/calatrava/templates/ios/test/AJAXConnectionTest.m +21 -0
  79. data/lib/calatrava/templates/ios/test/AppTests-Prefix.pch +8 -0
  80. data/lib/calatrava/templates/ios/test/CalatravaiOSTests-Info.plist +22 -0
  81. data/lib/calatrava/templates/ios/test/TWBridgePageRegistryTest.m +15 -0
  82. data/lib/calatrava/templates/ios/test/en.lproj/InfoPlist.strings +2 -0
  83. data/lib/calatrava/templates/kernel/.gitignore +1 -0
  84. data/lib/calatrava/templates/kernel/app/calatrava.coffee +8 -0
  85. data/lib/calatrava/templates/kernel/app/converter/controller.converter.coffee +50 -0
  86. data/lib/calatrava/templates/kernel/app/converter/init.converter.coffee +11 -0
  87. data/lib/calatrava/templates/kernel/app/pageHelper.coffee +17 -0
  88. data/lib/calatrava/templates/kernel/features/support/bridge.coffee +124 -0
  89. data/lib/calatrava/templates/kernel/features/support/spec_helper.js +49 -0
  90. data/lib/calatrava/templates/kernel/spec/converter/controller.converter.spec.coffee +37 -0
  91. data/lib/calatrava/templates/kernel/spec/environment.spec_helper.coffee +25 -0
  92. data/lib/calatrava/templates/kernel/spec/spec_helper.js +49 -0
  93. data/lib/calatrava/templates/kernel/spec/stubView.coffee +18 -0
  94. data/lib/calatrava/templates/kernel/watchr.rb +17 -0
  95. data/lib/calatrava/templates/package.json +20 -0
  96. data/lib/calatrava/templates/shell/layouts/single_page.haml +23 -0
  97. data/lib/calatrava/templates/shell/pages/converter/conversionForm.haml +12 -0
  98. data/lib/calatrava/templates/shell/pages/converter/page.conversionForm.coffee +42 -0
  99. data/lib/calatrava/templates/shell/shell.scss +1 -0
  100. data/lib/calatrava/templates/shell/support/shell.coffee +21 -0
  101. data/lib/calatrava/templates/web/apache/conf/mime.types +1357 -0
  102. data/lib/calatrava/templates/web/app/source/bridge.coffee +158 -0
  103. data/lib/calatrava/templates/web/app/source/init.coffee +14 -0
  104. data/lib/calatrava/templates/web/app/views/index.haml +18 -0
  105. data/lib/calatrava/templates/web/deploy/instance.sh +10 -0
  106. data/lib/calatrava/templates/web/manifest.yml +1 -0
  107. data/lib/calatrava/version.rb +3 -0
  108. data/lib/calatrava.rb +5 -0
  109. metadata +302 -0
@@ -0,0 +1,247 @@
1
+ package com.calatrava.shell;
2
+
3
+ import android.content.BroadcastReceiver;
4
+ import android.content.Context;
5
+ import android.content.Intent;
6
+ import android.content.IntentFilter;
7
+ import android.graphics.Color;
8
+ import android.util.Log;
9
+ import android.webkit.JsResult;
10
+ import android.webkit.WebChromeClient;
11
+ import android.webkit.WebView;
12
+ import android.webkit.WebViewClient;
13
+
14
+ import com.calatrava.bridge.RegisteredActivity;
15
+ import com.calatrava.bridge.RhinoService;
16
+ import com.calatrava.bridge.PageRegistry;
17
+
18
+ import java.util.HashMap;
19
+ import java.util.List;
20
+ import java.util.Map;
21
+ import java.util.concurrent.Callable;
22
+ import java.util.concurrent.FutureTask;
23
+ import java.util.concurrent.Semaphore;
24
+
25
+ public abstract class WebViewActivity extends RegisteredActivity {
26
+ private String TAG = WebViewActivity.class.getSimpleName();
27
+
28
+ private JSContainer jsContainer;
29
+ private WebView webView;
30
+ private boolean pageReady = false;
31
+ private RhinoService rhino;
32
+
33
+ private BroadcastReceiver receiver = new BroadcastReceiver() {
34
+ @Override
35
+ public void onReceive(Context context, Intent intent) {
36
+ if ("com.calatrava.dialog".equals(intent.getAction())) {
37
+ String name = intent.getExtras().getString("name");
38
+ webView.loadUrl("javascript:window." + getPageName() + "View.showDialog('" + name + "');");
39
+ }
40
+ }
41
+ };
42
+
43
+ @Override
44
+ protected void onRhinoConnected(RhinoService rhino) {
45
+ this.rhino = rhino;
46
+ jsContainer = new JSContainer(rhino, getPageName());
47
+ loadPage();
48
+ }
49
+
50
+ @Override
51
+ protected void onResume() {
52
+ super.onResume();
53
+
54
+ onPageLoadCompleted();
55
+
56
+ if (rhino != null) {
57
+ pageHasOpened();
58
+ }
59
+ registerReceiver(receiver, new IntentFilter("com.calatrava.dialog"));
60
+ }
61
+
62
+ @Override
63
+ protected void onPause() {
64
+ super.onPause();
65
+
66
+ PageRegistry.sharedRegistry().pageOffscreen(getPageName());
67
+ unregisterReceiver(receiver);
68
+ }
69
+
70
+ @Override
71
+ public void onDestroy() {
72
+ super.onDestroy();
73
+
74
+ PageRegistry.sharedRegistry().unregisterPage(getPageName());
75
+ }
76
+
77
+ public String getFieldValue(final String field) {
78
+ assert (getFields().contains(field));
79
+
80
+ Log.d(TAG, "Get value for field: " + field + " on page '" + getPageName() + "'");
81
+
82
+ FutureTask<String> fieldValue = new FutureTask<String>(new Callable<String>() {
83
+ public String call() throws Exception {
84
+ webView.loadUrl("javascript:container.provideValueFor('" + field + "', window." + getPageName() + "View.get('" + field + "'));");
85
+ return jsContainer.retrieveValueFor(field);
86
+ }
87
+ });
88
+
89
+ runOnUiThread(fieldValue);
90
+
91
+ String value;
92
+ try {
93
+ value = fieldValue.get();
94
+ } catch (Exception e) {
95
+ e.printStackTrace();
96
+ // TODO: Signal failure to the UI thread
97
+ return "";
98
+ }
99
+
100
+ Log.d(TAG, "(getFieldValue) : Got the field [" + field + "] value = " + value);
101
+ return value;
102
+ }
103
+
104
+ public void render(final String json) {
105
+ runOnUiThread(new Runnable() {
106
+ public void run() {
107
+ jsContainer.setJsObject(json);
108
+ Log.d(TAG, "render page: " + getPageName());
109
+
110
+ webView.loadUrl("javascript:container.onRenderComplete(window." + getPageName() + "View.render(JSON.parse(container.getJsObject())));");
111
+ }
112
+ });
113
+ }
114
+
115
+ protected abstract String getPageName();
116
+
117
+ protected abstract List<String> getEvents();
118
+
119
+ protected abstract List<String> getFields();
120
+
121
+ protected int getBackgroundColor(){
122
+ return Color.TRANSPARENT;
123
+ }
124
+
125
+ protected void loadPage() {
126
+ PageRegistry.sharedRegistry().registerPage(getPageName(), this);
127
+
128
+ webView = new WebView(this);
129
+ setContentView(webView);
130
+
131
+ webView.getSettings().setJavaScriptEnabled(true);
132
+ webView.getSettings().setDomStorageEnabled(true);
133
+ webView.setScrollBarStyle(webView.SCROLLBARS_OUTSIDE_OVERLAY);
134
+ webView.setScrollbarFadingEnabled(true);
135
+ webView.setBackgroundColor(0xffffffff);
136
+ webView.addJavascriptInterface(jsContainer, "container");
137
+
138
+ webView.setWebViewClient(new WebViewClient() {
139
+ @Override
140
+ public void onPageFinished(WebView view, String url) {
141
+ super.onPageFinished(view, url);
142
+ Log.d(TAG, "Webview finished loading a URL");
143
+
144
+ pageReady = true;
145
+ onPageLoadCompleted();
146
+ }
147
+ });
148
+
149
+ webView.setWebChromeClient(new WebChromeClient() {
150
+ @Override
151
+ public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
152
+ Log.d(TAG, "Received JS alert: '" + message + "'");
153
+ return false;
154
+ }
155
+ });
156
+
157
+ webView.loadUrl("file:///android_asset/hybrid/views/" + getPageName() + ".html");
158
+ pageHasOpened();
159
+ }
160
+
161
+ private void pageHasOpened() {
162
+ rhino.triggerEvent(getPageName(), "pageOpened", new String[] {});
163
+ }
164
+
165
+ private void onPageLoadCompleted() {
166
+ if (jsContainer != null && pageReady) {
167
+ jsContainer.onRenderComplete(null);
168
+
169
+ for (String field : getFields()) {
170
+ jsContainer.hasField(field);
171
+ }
172
+
173
+ PageRegistry.sharedRegistry().pageOnscreen(getPageName());
174
+ }
175
+ }
176
+
177
+ public class JSContainer {
178
+ private String TAG = JSContainer.class.getSimpleName();
179
+
180
+ private Map<String, String> lastValue = new HashMap<String, String>();
181
+ private Map<String, Semaphore> valueAvailable = new HashMap<String, Semaphore>();
182
+ private RhinoService rhino;
183
+ private String pageName;
184
+ private String jsObject;
185
+
186
+ public JSContainer(RhinoService rhino, String pageName) {
187
+ this.rhino = rhino;
188
+ this.pageName = pageName;
189
+ }
190
+
191
+ public void setJsObject(String jsObject) {
192
+ this.jsObject = jsObject;
193
+ }
194
+
195
+ public String getJsObject() {
196
+ return jsObject;
197
+ }
198
+
199
+ public void hasField(String field) {
200
+ valueAvailable.put(field, new Semaphore(0));
201
+ }
202
+
203
+ public void provideValueFor(String field, String value) {
204
+ Log.d(TAG, "Got value '" + value + "' for field '" + field + "'");
205
+ lastValue.put(field, value);
206
+ valueAvailable.get(field).release();
207
+ }
208
+
209
+ public String retrieveValueFor(String field) {
210
+ try {
211
+ valueAvailable.get(field).acquire();
212
+ } catch (InterruptedException e) {
213
+ return retrieveValueFor(field);
214
+ }
215
+
216
+ String value = lastValue.remove(field);
217
+ Log.d(TAG, "Reading value '" + value + "' for field '" + field + "'");
218
+ return value;
219
+ }
220
+
221
+ public void handleEvent(String event, String... extraArgs) {
222
+ Log.d(TAG, "User clicked");
223
+ Log.d(TAG, "Current thread is " + Thread.currentThread().getId());
224
+
225
+ if (extraArgs != null) {
226
+ Log.d(TAG, "extraArgs = " + extraArgs.length);
227
+ for (String arg : extraArgs) {
228
+ Log.d(TAG, "arg = '" + arg + "'");
229
+ }
230
+ } else {
231
+ Log.d(TAG, "extraArgs were null!");
232
+ }
233
+ rhino.triggerEvent(pageName, event, extraArgs);
234
+ }
235
+
236
+ public void onRenderComplete(Object ignored) {
237
+ runOnUiThread(new Runnable() {
238
+ public void run() {
239
+ for (String event : getEvents()) {
240
+ Log.d(TAG, "About to bind event '" + event + "'");
241
+ webView.loadUrl("javascript:window." + getPageName() + "View.bind('" + event + "', function() { container.handleEvent('" + event + "', _.toArray(arguments)); });");
242
+ }
243
+ }
244
+ });
245
+ }
246
+ }
247
+ }
@@ -0,0 +1 @@
1
+ - converter
@@ -0,0 +1,5 @@
1
+ platform :ios, '5.0'
2
+
3
+ xcodeproj '{{ project_name}}.xcodeproj'
4
+
5
+ pod 'calatrava', :git => 'https://github.com/calatrava/calatrava-ios'
@@ -0,0 +1 @@
1
+ - converter
@@ -0,0 +1,249 @@
1
+ var tw = tw || {};
2
+ tw.bridge = tw.bridge || {};
3
+
4
+ // json.js does stupid things to the Object prototype
5
+ Object.prototype.toJSONString = "";
6
+ Object.prototype.parseJSON = "";
7
+
8
+ function generateRandomString(){
9
+ var str = '';
10
+ var i;
11
+ for (i = 0; i < 32; ++i) {
12
+ var r = Math.floor(Math.random() * 16);
13
+ str = str + r.toString(16);
14
+ }
15
+ return str.toUpperCase();
16
+ }
17
+
18
+ function bridgeDispatch(proxyId) {
19
+ var extraArgs = _.map(_.toArray(arguments).slice(1), function(obj) { return obj.valueOf(); });
20
+ var proxyPage = tw.bridge.pages.pageByProxyId(proxyId);
21
+ proxyPage.dispatch.apply(proxyPage, extraArgs);
22
+ }
23
+
24
+ function bridgeSuccessfulResponse(requestId, response) {
25
+ tw.bridge.requests.successfulResponse(requestId, response);
26
+ }
27
+
28
+ function bridgeFailureResponse(requestId, errorCode, response) {
29
+ tw.bridge.requests.failureResponse(requestId, errorCode, response);
30
+ }
31
+
32
+ function bridgeFireTimer(timerId) {
33
+ tw.bridge.timers.fireTimer(timerId);
34
+ }
35
+
36
+ function bridgeInvokeCallback(widgetName) {
37
+ var extraArgs = _.map(_.toArray(arguments).slice(1), function(obj) { return obj.valueOf(); });
38
+ tw.bridge.widgets.callback(widgetName).apply(this, extraArgs);
39
+ }
40
+
41
+ tw.bridge.changePage = function(target) {
42
+ tw.bridge.runtime.changePage(target);
43
+ return target;
44
+ };
45
+
46
+ tw.bridge.widgets = (function() {
47
+ var callbacks;
48
+ callbacks = {};
49
+ return {
50
+ display: function(name, options, callback) {
51
+ TWBridgePageRegistry.sharedRegistry.displayWidget_withOptions(name, options);
52
+ return callbacks[name] = callback;
53
+ },
54
+ callback: function(name) {
55
+ return callbacks[name];
56
+ }
57
+ };
58
+ })();
59
+
60
+ tw.bridge.alert = function(message) {
61
+ TWBridgePageRegistry.sharedRegistry.alert(message);
62
+ };
63
+
64
+ tw.bridge.openUrl = function(url) {
65
+ tw.bridge.runtime.openUrl(url);
66
+ };
67
+
68
+ tw.bridge.trackEvent = function(event) {
69
+ };
70
+
71
+ tw.bridge.log = function(message) {
72
+ tw.bridge.runtime.log(message);
73
+ };
74
+
75
+ tw.bridge.request = function(options) {
76
+ if (options.contentType) {
77
+ options.customHeaders = options.customHeaders || {};
78
+ options.customHeaders['Content-Type'] = options.contentType;
79
+ }
80
+ tw.bridge.requests.issue(
81
+ options.url,
82
+ options.method,
83
+ options.body,
84
+ options.success,
85
+ options.failure,
86
+ options.customHeaders);
87
+ };
88
+
89
+ tw.bridge.pageObject = function(pageName) {
90
+ var proxyId = generateRandomString(),
91
+ handlerRegistry = {};
92
+
93
+ tw.bridge.runtime.registerProxyForPage(proxyId, pageName);
94
+
95
+ function bind(event, handler) {
96
+ handlerRegistry[event] = handler;
97
+ tw.bridge.runtime.attachProxyEventHandler(proxyId, event);
98
+ }
99
+
100
+ function dispatch(event) {
101
+ args = _.toArray(arguments).slice(1);
102
+ if (handlerRegistry[event]) {
103
+ handlerRegistry[event].apply(this, args);
104
+ }
105
+ }
106
+
107
+ function get(field) {
108
+ return tw.bridge.runtime.valueOfProxyField(proxyId, field);
109
+ }
110
+
111
+ function cleanValues(jsObject) {
112
+ _.each(_.keys(jsObject), function(key) {
113
+ if (jsObject[key] === null || jsObject[key] === undefined) {
114
+ delete jsObject[key];
115
+ } else if (jsObject[key] === false) {
116
+ jsObject[key] = 0;
117
+ } else {
118
+ tw.bridge.log("key = '" + key + "'; value = '" + jsObject[key] + "'");
119
+ if (jsObject[key] instanceof Object) {
120
+ cleanValues(jsObject[key]);
121
+ }
122
+ }
123
+ });
124
+ }
125
+
126
+ function render(viewObject) {
127
+ // Clean off properties that cause problems when marshalling
128
+ if (viewObject.hasOwnProperty('toJSONString')) {
129
+ viewObject.toJSONString = null;
130
+ }
131
+
132
+ // Delete any keys that have a null value to avoid the Obj-C JSON
133
+ // serialization failure
134
+ if (viewObject != undefined) {
135
+ cleanValues(viewObject);
136
+ }
137
+ if (viewObject.hasOwnProperty('parseJSON')) {
138
+ viewObject.parseJSON = null;
139
+ }
140
+
141
+ tw.bridge.runtime.renderProxy(viewObject, proxyId);
142
+ }
143
+
144
+ return {
145
+ proxyId: proxyId,
146
+ bind: bind,
147
+ dispatch: dispatch,
148
+ get: get,
149
+ render: render
150
+ };
151
+ };
152
+
153
+ tw.bridge.pages = (function() {
154
+ var pagesByName = {},
155
+ pagesByProxyId = {};
156
+
157
+ function pageNamed(pageName) {
158
+ if (!pagesByName[pageName]) {
159
+ var page = tw.bridge.pageObject(pageName);
160
+ pagesByName[pageName] = page;
161
+ pagesByProxyId[page.proxyId] = page;
162
+ }
163
+ return pagesByName[pageName];
164
+ }
165
+
166
+ function pageByProxyId(proxyId) {
167
+ return pagesByProxyId[proxyId];
168
+ }
169
+
170
+ return {
171
+ pageNamed: pageNamed,
172
+ pageByProxyId: pageByProxyId
173
+ };
174
+ }());
175
+
176
+ tw.bridge.requests = (function() {
177
+ var successHandlersById = {},
178
+ failureHandlersById = {};
179
+
180
+ function issue(url, method, body, success, failure, customHeaders) {
181
+ var requestId = generateRandomString();
182
+ bodyStr = body;
183
+
184
+ if (bodyStr && bodyStr.constructor !== String) {
185
+ bodyStr = JSON.stringify(body);
186
+ }
187
+
188
+ successHandlersById[requestId] = success;
189
+ failureHandlersById[requestId] = failure;
190
+ tw.bridge.runtime.issueRequest({
191
+ requestId: requestId,
192
+ url: url,
193
+ method: method,
194
+ body: bodyStr,
195
+ headers: customHeaders
196
+ });
197
+ }
198
+
199
+ function successfulResponse(requestId, response) {
200
+ successHandlersById[requestId](response);
201
+ clearHandlers(requestId);
202
+ }
203
+
204
+ function failureResponse(requestId, errorCode, response) {
205
+ failureHandlersById[requestId](errorCode, response);
206
+ clearHandlers(requestId);
207
+ }
208
+
209
+ function clearHandlers(requestId) {
210
+ successHandlersById[requestId] = null;
211
+ failureHandlersById[requestId] = null;
212
+ }
213
+
214
+ return {
215
+ successfulResponse: successfulResponse,
216
+ failureResponse: failureResponse,
217
+ issue: issue
218
+ };
219
+ }());
220
+
221
+ tw.bridge.timers = (function () {
222
+ callbacks = {};
223
+
224
+ return {
225
+ start: function(timeout, callback) {
226
+ var timerId = generateRandomString();
227
+ callbacks[timerId] = callback;
228
+ tw.bridge.runtime.startTimerWithTimeout(timerId, timeout);
229
+ return timerId;
230
+ },
231
+ fireTimer: function(timerId) {
232
+ if (callbacks[timerId]) {
233
+ callbacks[timerId]();
234
+ }
235
+ },
236
+ clearTimer: function(timerId) {
237
+ delete callbacks[timerId];
238
+ }
239
+ };
240
+ }());
241
+
242
+ tw.bridge.dialog = (function() {
243
+ return {
244
+ display: function(name) {
245
+ TWBridgePageRegistry.sharedRegistry.displayDialog(name)
246
+ }
247
+ };
248
+ }());
249
+